C++结构体数组文件读写功能异常排查求助
问题分析与解决方案
看起来你遇到的核心问题是结构体里的std::string成员导致二进制读写失效,另外代码里还有几个小细节也会影响读取结果,我来一步步给你拆解:
1. 最关键的问题:std::string不能直接二进制读写
你的property结构体里有string adress,std::string本质上是一个包含指针、长度等成员的容器,它的实际字符串内容存在堆内存里。当你用fp.write((char*)bDanni, sizeof(property)*n)直接写整个结构体时,写入的只是string内部的指针值,而不是实际的地址字符串内容。等你读取的时候,这个指针指向的内存早就失效了,自然读不到正确的内容。
解决这个问题有两种简单的方式:
方式一:把std::string换成固定长度的char数组
这是最适合新手的方案,因为这样整个结构体就变成了POD(Plain Old Data)类型,可以直接安全地进行二进制读写。修改结构体:
struct property { int num; char nBrok[50]; char type[10]; char adress[100]; // 替换string为固定大小char数组 char outlook[20]; double price; double size; int nRooms; int floor; int status; };
这样原来的读写逻辑就可以正常工作了,只是你在给adress赋值的时候要注意用strcpy或者cin.getline这类适合char数组的方式。
方式二:手动序列化/反序列化结构体(保留std::string)
如果你一定要用std::string,就需要逐个成员读写,单独处理字符串:
// 修改写入函数 void fileWrite(property bDanni[], int n) { ofstream fp("dbase.dat", ios::binary); // 用局部ofstream,避免全局变量 if (!fp) { cout << "\n Error in file \n"; exit(1); } for(int i=0; i<n; i++){ fp.write((char*)&bDanni[i].num, sizeof(int)); fp.write(bDanni[i].nBrok, sizeof(bDanni[i].nBrok)); fp.write(bDanni[i].type, sizeof(bDanni[i].type)); // 处理string:先写长度,再写内容 size_t len = bDanni[i].adress.size(); fp.write((char*)&len, sizeof(len)); fp.write(bDanni[i].adress.c_str(), len); fp.write(bDanni[i].outlook, sizeof(bDanni[i].outlook)); fp.write((char*)&bDanni[i].price, sizeof(double)); fp.write((char*)&bDanni[i].size, sizeof(double)); fp.write((char*)&bDanni[i].nRooms, sizeof(int)); fp.write((char*)&bDanni[i].floor, sizeof(int)); fp.write((char*)&bDanni[i].status, sizeof(int)); } fp.close(); } // 修改读取函数 int fileRead(property bDanni[]) { ifstream fp("dbase.dat", ios::binary); // 局部ifstream if (!fp) { cout << "\n file does not exist\n"; return 0; } int n = 0; while(true){ property b; // 逐个读成员 if(!fp.read((char*)&b.num, sizeof(int))) break; fp.read(b.nBrok, sizeof(b.nBrok)); fp.read(b.type, sizeof(b.type)); // 处理string:先读长度,再读内容 size_t len; fp.read((char*)&len, sizeof(len)); char* buf = new char[len+1]; fp.read(buf, len); buf[len] = '\0'; b.adress = buf; delete[] buf; fp.read(b.outlook, sizeof(b.outlook)); fp.read((char*)&b.price, sizeof(double)); fp.read((char*)&b.size, sizeof(double)); fp.read((char*)&b.nRooms, sizeof(int)); fp.read((char*)&b.floor, sizeof(int)); fp.read((char*)&b.status, sizeof(int)); bDanni[n++] = b; // 防止数组越界 if(n >=100) break; } fp.close(); return n; }
2. 其他需要修正的小问题
- 全局
fstream fp的风险:全局流对象容易因为多次打开/关闭出现状态错误,建议在每个函数里使用局部的ofstream/ifstream,更安全。 - main函数里的局部变量覆盖:你在
if(answer == 'y')里定义了int total = fileRead(bDanni);,这里的total是局部变量,外面的total还是0。应该改成:
if (answer == 'y') { total = fileRead(bDanni); // 去掉前面的int,赋值给全局的total }
- fileRead函数的冗余操作:原来的代码先打开文件求大小,关闭再打开读取,其实可以直接
seekg(0)回到文件开头,不用重新打开:
// 优化后的fileRead(针对POD结构体的版本) int fileRead(property bDanni[]) { ifstream fp("dbase.dat", ios::binary); if (!fp) { cout << "\n file does not exist\n"; return 0; } // 计算元素个数 fp.seekg(0, ios::end); long pos = fp.tellg(); int n = pos / sizeof(property); fp.seekg(0, ios::beg); // 直接回到开头,不用关闭重开 for (int i = 0; i < n; i++) { fp.read((char*)&bDanni[i], sizeof(property)); } fp.close(); return n; }
测试建议
修改完代码后,先确保写入的是正确的POD结构体(比如用char数组替代string),然后读取后可以打印几个成员变量看看是否正确,比如:
if (answer == 'y') { total = fileRead(bDanni); cout << "读取到" << total << "条数据\n"; for(int i=0; i<total; i++){ cout << "第" << i+1 << "条地址:" << bDanni[i].adress << "\n"; } }
内容的提问来源于stack exchange,提问作者Levianeer
相关产品推荐
相关产品推荐

