使用二进制文件访问违规读取位置
Access violation reading location using binary file
首先,我知道有类似问题的帖子,但我无法在其中找到适合我的解决方案。
这是一个编程作业,使用二进制和文本文件存储"Corporate sales data."(部门名称、季度和销售额),然后在二进制数据文件中搜索指定的记录并显示它们。
以下是我的代码的重要部分:
#include stuff
...
// Struct to hold division data
struct DIVISION_DATA_S
{
string divisionName;
int quarter;
double sales;
};
int main()
{
...
// Open the data file
fstream dataFile;
dataFile.open(dataFilePath, ios::in | ios::out | ios::binary);
... Get data from user, store in an instance of my struct ...
// Dump struct into binary file
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
// Cycle through the targets file and display the record from divisiondata.dat for each entry
while(targetsFile >> targetDivisionName)
{
int targetQuarter; // Target quarter
string targetQuarterStr;
targetsFile.ignore(); // Ignore the residual '\n' from the ">>" read
getline(targetsFile, targetQuarterStr);
targetQuarter = atoi(targetQuarterStr.c_str()); // Parses into an int
cout << "Target: " << targetDivisionName << " " << targetQuarter << endl;
// Linear search the data file for the required name and quarter to find sales amount
double salesOfTarget;
bool isFound = false;
while (!isFound && !dataFile.eof())
{
cout << "Found division data: " << targetDivisionName << " " << targetQuarter << endl;
DIVISION_DATA_S divisionData;
// Read an object from the file, cast as DIVISION_DATA_S
dataFile.read(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
cout << "Successfully read data for " << targetDivisionName << " " << targetQuarter << endl
<< "Name: " << divisionData.divisionName << ", Q: " << divisionData.quarter << ", "
<< "Sales: " << divisionData.sales << endl;
// Test for a match of both fields
if (divisionData.divisionName == targetDivisionName && divisionData.quarter == targetQuarter)
{
isFound = true;
cout << "Match!" << endl;
salesOfTarget = divisionData.sales;
}
}
if (!isFound) // Error message if record is not found in data file
{
cout << "\nError. Could not find record for " << targetDivisionName
<< " division, quarter " << targetQuarter << endl;
}
else
{
// Display the corresponding record
cout << "Division: " << targetDivisionName << ", Quarter: " << targetQuarter
<< "Sales: " << salesOfTarget << endl;
totalSales += salesOfTarget; // Add current sales to the sales accumulator
numberOfSalesFound++; // Increment total number of sales found
}
}
抱歉 while 循环缺少缩进,copy/paste 搞砸了。
我的问题出现在尝试访问从二进制文件读取的信息时。例如,当它试图执行我为调试添加的 cout
语句时,它给我这个错误:
Unhandled exception at 0x0FED70B6 (msvcp140d.dll) in CorporateSalesData.exe: 0xC0000005: Access violation reading location 0x310A0D68.
现在,从我读到的内容来看,这似乎意味着某些东西正试图从内存的极低区域读取,也就是某处某处与空指针有关,但我无法想象如何那会出现。整个读取操作完全是从我的教科书中复制的,我不知道 reinterpret_chast
是什么,更不用说它是如何工作的或者如何用它修复错误了。请帮忙?
编辑:感谢所有帮助。为了避免复杂化或使用我不完全理解的东西,我将切换到 divisionName 的 c 字符串。
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
仅当您有 POD 类型时才有效。当你有 std::string
时它不起作用。您需要使用以下内容:
// Write the size of the string.
std::string::size_type size = divisionDat.divisionName.size();
dataFile.write(reinterpret_cast<char*>(&size), sizeof(size));
// Now write the string.
dataFile.write(reinterpret_cast<char*>(divisionDat.divisionName.c_str()), size);
// Write the quarter and the sales.
dataFile.write(reinterpret_cast<char*>(&divisionDat.quarter), sizeof(divisionDat.quarter));
dataFile.write(reinterpret_cast<char*>(&divisionDat.sales), sizeof(divisionDat.sales));
更改读取调用以匹配写入调用。
// Dump struct into binary file
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
/*...*/
// Read an object from the file, cast as DIVISION_DATA_S
dataFile.read(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
这在任何情况下都绝对无效。
std::string
使用堆分配的指针来存储它包含的任何字符串数据。您写入文件的内容 不是 字符串的内容,而只是字符串数据所在的地址(以及一些元数据)。如果您任意读取这些指针并将它们视为内存(就像您在 cout
语句中一样),您将引用已删除的内存。
你有两个选择。
如果你想要的只是一个可以轻松序列化的结构,那么只需像这样转换它:
// Struct to hold division data
struct DIVISION_DATA_S
{
char divisionName[500];
int quarter;
double sales;
};
当然,使用这种样式,您只能与作为 C 字符串的名称进行交互,并且也被限制在 500 个字符以内。
另一种选择是正确地序列化这个对象。
// Struct to hold division data
struct DIVISION_DATA_S
{
string divisionName;
int quarter;
double sales;
string serialize() const { //Could also have the signature be std::vector<char>, but this will make writing with it easier.
string output;
std::array<char, 8> size_array;
size_t size_of_string = divisionName.size();
for(char & c : size_array) {
c = size_of_string & 0xFF;
size_of_string >>= 8;
}
output.insert(output.end(), size_array.begin(), size_array.end());
output.insert(output.end(), divisionName.begin(), divisionName.end());
int temp_quarter = quarter;
for(char & c : size_array) {
c = temp_quarter & 0xFF;
temp_quarter >>= 8;
}
output.insert(output.end(), size_array.begin(), size_array.begin() + sizeof(int));
size_t temp_sales = reinterpret_cast<size_t>(sales);
for(char & c : size_array) {
c = temp_sales & 0xFF;
temp_sales >>= 8;
}
output.insert(output.end(), size_array.begin(), size_array.end());
return output;
}
size_t unserialize(const string & input) {
size_t size_of_string = 0;
for(int i = 7; i >= 0; i--) {
size_of_string <<= 8;
size_of_string += unsigned char(input[i]);
}
divisionName = input.substr(7, 7 + size_of_string);
quarter = 0;
for(int i = 10 + size_of_string; i >= 7 + size_of_string; i--) {
quarter <<= 8;
quarter += unsigned char(input[i]);
}
size_t temp_sales = 0;
for(int i = 18 + size_of_string; i >= 11 + size_of_string; i--) {
temp_sales <<= 8;
temp_sales += unsigned char(input[i]);
}
sales = reinterpret_cast<double>(temp_sales);
return 8 + size_of_string + 4 + 8;
}
};
写入文件非常简单:
dataFile << divisionData.serialize();
阅读可以稍微难一点:
stringstream ss;
ss << dataFile.rdbuf();
string file_data = ss.str();
size_t size = divisionData.unserialize(file_data);
file_data = file_data.substr(size);
size = divisionData.unserialize(file_data);
/*...*/
顺便说一句,我还没有检查我的代码的语法或完整性。此示例旨在作为您需要为正确 serialize/unserialize 复杂对象编写的代码类型的参考。我相信它是正确的,但我不会未经测试就扔掉它。
欢迎来到连载的世界。您正在尝试 'bit blit' 您的结构到一个文件中。这仅适用于数据实际上是内联的非常简单的类型(int、float、char[xxx])。即使它确实有效,您也不得不将数据重新加载到相同类型的机器中(相同的字长,相同的字节顺序)。
您需要做的是将数据序列化,然后反序列化。您可以自己发明方法,也可以在许多 标准中使用一种方法。有两种基本类型——二进制(高效,人类无法阅读)和文本(效率较低但人类可读)
文字
- json
- yaml
- xml
- csv
二进制
- protobuf
boost 有一个序列化库 http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/
你也可以看看这里
首先,我知道有类似问题的帖子,但我无法在其中找到适合我的解决方案。
这是一个编程作业,使用二进制和文本文件存储"Corporate sales data."(部门名称、季度和销售额),然后在二进制数据文件中搜索指定的记录并显示它们。
以下是我的代码的重要部分:
#include stuff
...
// Struct to hold division data
struct DIVISION_DATA_S
{
string divisionName;
int quarter;
double sales;
};
int main()
{
...
// Open the data file
fstream dataFile;
dataFile.open(dataFilePath, ios::in | ios::out | ios::binary);
... Get data from user, store in an instance of my struct ...
// Dump struct into binary file
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
// Cycle through the targets file and display the record from divisiondata.dat for each entry
while(targetsFile >> targetDivisionName)
{
int targetQuarter; // Target quarter
string targetQuarterStr;
targetsFile.ignore(); // Ignore the residual '\n' from the ">>" read
getline(targetsFile, targetQuarterStr);
targetQuarter = atoi(targetQuarterStr.c_str()); // Parses into an int
cout << "Target: " << targetDivisionName << " " << targetQuarter << endl;
// Linear search the data file for the required name and quarter to find sales amount
double salesOfTarget;
bool isFound = false;
while (!isFound && !dataFile.eof())
{
cout << "Found division data: " << targetDivisionName << " " << targetQuarter << endl;
DIVISION_DATA_S divisionData;
// Read an object from the file, cast as DIVISION_DATA_S
dataFile.read(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
cout << "Successfully read data for " << targetDivisionName << " " << targetQuarter << endl
<< "Name: " << divisionData.divisionName << ", Q: " << divisionData.quarter << ", "
<< "Sales: " << divisionData.sales << endl;
// Test for a match of both fields
if (divisionData.divisionName == targetDivisionName && divisionData.quarter == targetQuarter)
{
isFound = true;
cout << "Match!" << endl;
salesOfTarget = divisionData.sales;
}
}
if (!isFound) // Error message if record is not found in data file
{
cout << "\nError. Could not find record for " << targetDivisionName
<< " division, quarter " << targetQuarter << endl;
}
else
{
// Display the corresponding record
cout << "Division: " << targetDivisionName << ", Quarter: " << targetQuarter
<< "Sales: " << salesOfTarget << endl;
totalSales += salesOfTarget; // Add current sales to the sales accumulator
numberOfSalesFound++; // Increment total number of sales found
}
}
抱歉 while 循环缺少缩进,copy/paste 搞砸了。
我的问题出现在尝试访问从二进制文件读取的信息时。例如,当它试图执行我为调试添加的 cout
语句时,它给我这个错误:
Unhandled exception at 0x0FED70B6 (msvcp140d.dll) in CorporateSalesData.exe: 0xC0000005: Access violation reading location 0x310A0D68.
现在,从我读到的内容来看,这似乎意味着某些东西正试图从内存的极低区域读取,也就是某处某处与空指针有关,但我无法想象如何那会出现。整个读取操作完全是从我的教科书中复制的,我不知道 reinterpret_chast
是什么,更不用说它是如何工作的或者如何用它修复错误了。请帮忙?
编辑:感谢所有帮助。为了避免复杂化或使用我不完全理解的东西,我将切换到 divisionName 的 c 字符串。
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
仅当您有 POD 类型时才有效。当你有 std::string
时它不起作用。您需要使用以下内容:
// Write the size of the string.
std::string::size_type size = divisionDat.divisionName.size();
dataFile.write(reinterpret_cast<char*>(&size), sizeof(size));
// Now write the string.
dataFile.write(reinterpret_cast<char*>(divisionDat.divisionName.c_str()), size);
// Write the quarter and the sales.
dataFile.write(reinterpret_cast<char*>(&divisionDat.quarter), sizeof(divisionDat.quarter));
dataFile.write(reinterpret_cast<char*>(&divisionDat.sales), sizeof(divisionDat.sales));
更改读取调用以匹配写入调用。
// Dump struct into binary file
dataFile.write(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
/*...*/
// Read an object from the file, cast as DIVISION_DATA_S
dataFile.read(reinterpret_cast<char *>(&divisionData), sizeof(divisionData));
这在任何情况下都绝对无效。
std::string
使用堆分配的指针来存储它包含的任何字符串数据。您写入文件的内容 不是 字符串的内容,而只是字符串数据所在的地址(以及一些元数据)。如果您任意读取这些指针并将它们视为内存(就像您在 cout
语句中一样),您将引用已删除的内存。
你有两个选择。
如果你想要的只是一个可以轻松序列化的结构,那么只需像这样转换它:
// Struct to hold division data
struct DIVISION_DATA_S
{
char divisionName[500];
int quarter;
double sales;
};
当然,使用这种样式,您只能与作为 C 字符串的名称进行交互,并且也被限制在 500 个字符以内。
另一种选择是正确地序列化这个对象。
// Struct to hold division data
struct DIVISION_DATA_S
{
string divisionName;
int quarter;
double sales;
string serialize() const { //Could also have the signature be std::vector<char>, but this will make writing with it easier.
string output;
std::array<char, 8> size_array;
size_t size_of_string = divisionName.size();
for(char & c : size_array) {
c = size_of_string & 0xFF;
size_of_string >>= 8;
}
output.insert(output.end(), size_array.begin(), size_array.end());
output.insert(output.end(), divisionName.begin(), divisionName.end());
int temp_quarter = quarter;
for(char & c : size_array) {
c = temp_quarter & 0xFF;
temp_quarter >>= 8;
}
output.insert(output.end(), size_array.begin(), size_array.begin() + sizeof(int));
size_t temp_sales = reinterpret_cast<size_t>(sales);
for(char & c : size_array) {
c = temp_sales & 0xFF;
temp_sales >>= 8;
}
output.insert(output.end(), size_array.begin(), size_array.end());
return output;
}
size_t unserialize(const string & input) {
size_t size_of_string = 0;
for(int i = 7; i >= 0; i--) {
size_of_string <<= 8;
size_of_string += unsigned char(input[i]);
}
divisionName = input.substr(7, 7 + size_of_string);
quarter = 0;
for(int i = 10 + size_of_string; i >= 7 + size_of_string; i--) {
quarter <<= 8;
quarter += unsigned char(input[i]);
}
size_t temp_sales = 0;
for(int i = 18 + size_of_string; i >= 11 + size_of_string; i--) {
temp_sales <<= 8;
temp_sales += unsigned char(input[i]);
}
sales = reinterpret_cast<double>(temp_sales);
return 8 + size_of_string + 4 + 8;
}
};
写入文件非常简单:
dataFile << divisionData.serialize();
阅读可以稍微难一点:
stringstream ss;
ss << dataFile.rdbuf();
string file_data = ss.str();
size_t size = divisionData.unserialize(file_data);
file_data = file_data.substr(size);
size = divisionData.unserialize(file_data);
/*...*/
顺便说一句,我还没有检查我的代码的语法或完整性。此示例旨在作为您需要为正确 serialize/unserialize 复杂对象编写的代码类型的参考。我相信它是正确的,但我不会未经测试就扔掉它。
欢迎来到连载的世界。您正在尝试 'bit blit' 您的结构到一个文件中。这仅适用于数据实际上是内联的非常简单的类型(int、float、char[xxx])。即使它确实有效,您也不得不将数据重新加载到相同类型的机器中(相同的字长,相同的字节顺序)。
您需要做的是将数据序列化,然后反序列化。您可以自己发明方法,也可以在许多 标准中使用一种方法。有两种基本类型——二进制(高效,人类无法阅读)和文本(效率较低但人类可读)
文字
- json
- yaml
- xml
- csv
二进制
- protobuf
boost 有一个序列化库 http://www.boost.org/doc/libs/1_61_0/libs/serialization/doc/
你也可以看看这里