OOP项目中读取含vector<string>的struct二进制文件出错求助
问题排查与修复方案
核心问题:数据库初始化时的记录数量计算错误
Database类构造函数中,你用文件总大小除以sizeof(structalquiler)来计算记录数,这是完全错误的。每个Alquiler实例写入文件的字节数不固定——vector<string>部分的长度取决于字符串的数量和每个字符串的长度,每条记录的总字节数是固定基础数据大小加上可变长度的向量数据,而非structalquiler的固定大小。这会导致你错误计算记录数量,读取时越界读取数据,进而触发乱码或内存错误。
次要问题:冗余的structalquiler结构
structalquiler结构体包含vector<string>,但写入时你并未直接序列化该vector,而是手动处理。这个结构体属于冗余设计,且sizeof(reg) - sizeof(reg.artlist)的写法不够直观,容易引发错误。
修复步骤与完整代码
1. 修正记录数计算逻辑
写入文件时先写入记录总数,读取时先读取总数再循环读取对应数量的记录,彻底抛弃通过文件大小计算记录数的错误方式。
2. 简化序列化逻辑
直接使用Alquiler类的成员变量进行序列化,删除冗余的structalquiler结构体。
3. 修复后的完整代码
#include <iostream> #include <vector> #include <string> #include <fstream> using namespace std; struct Date { int day, month, year; }; class Alquiler { private: int a_DNI_Cliente; int a_Total_Alquiler, a_Deposito; Date a_Date_Alquiler, a_Date_Devolucion; vector<string> a_Article_list; public: Alquiler () = default; Alquiler (int dni, int totalq, int dep, Date fa, Date fd, vector<string> arts) : a_DNI_Cliente(dni), a_Total_Alquiler(totalq), a_Deposito(dep), a_Date_Alquiler(fa), a_Date_Devolucion(fd), a_Article_list(arts) {} void save(ofstream &outfile) const { // 写入固定长度基础数据 outfile.write(reinterpret_cast<const char*>(&a_DNI_Cliente), sizeof(a_DNI_Cliente)); outfile.write(reinterpret_cast<const char*>(&a_Total_Alquiler), sizeof(a_Total_Alquiler)); outfile.write(reinterpret_cast<const char*>(&a_Deposito), sizeof(a_Deposito)); outfile.write(reinterpret_cast<const char*>(&a_Date_Alquiler), sizeof(a_Date_Alquiler)); outfile.write(reinterpret_cast<const char*>(&a_Date_Devolucion), sizeof(a_Date_Devolucion)); // 序列化vector<string> size_t vec_size = a_Article_list.size(); outfile.write(reinterpret_cast<const char*>(&vec_size), sizeof(vec_size)); for (const auto& str : a_Article_list) { size_t str_len = str.length(); outfile.write(reinterpret_cast<const char*>(&str_len), sizeof(str_len)); outfile.write(str.c_str(), str_len); } } void read(ifstream &infile) { // 读取固定长度基础数据 infile.read(reinterpret_cast<char*>(&a_DNI_Cliente), sizeof(a_DNI_Cliente)); infile.read(reinterpret_cast<char*>(&a_Total_Alquiler), sizeof(a_Total_Alquiler)); infile.read(reinterpret_cast<char*>(&a_Deposito), sizeof(a_Deposito)); infile.read(reinterpret_cast<char*>(&a_Date_Alquiler), sizeof(a_Date_Alquiler)); infile.read(reinterpret_cast<char*>(&a_Date_Devolucion), sizeof(a_Date_Devolucion)); // 反序列化vector<string> size_t vec_size; infile.read(reinterpret_cast<char*>(&vec_size), sizeof(vec_size)); a_Article_list.clear(); a_Article_list.reserve(vec_size); for (size_t i = 0; i < vec_size; ++i) { size_t str_len; infile.read(reinterpret_cast<char*>(&str_len), sizeof(str_len)); string str(str_len, '\0'); infile.read(&str[0], str_len); a_Article_list.push_back(std::move(str)); } } // 测试用:打印当前实例数据 void print() const { cout << "DNI: " << a_DNI_Cliente << endl; cout << "Total: " << a_Total_Alquiler << ", Deposito: " << a_Deposito << endl; cout << "Fecha Alquiler: " << a_Date_Alquiler.day << "/" << a_Date_Alquiler.month << "/" << a_Date_Alquiler.year << endl; cout << "Fecha Devolucion: " << a_Date_Devolucion.day << "/" << a_Date_Devolucion.month << "/" << a_Date_Devolucion.year << endl; cout << "Articulos: "; for (const auto& art : a_Article_list) { cout << art << " "; } cout << endl << endl; } }; class Database { private: string m_file_name = "alquileres.dat"; vector<Alquiler> Arreglo_Alquileres; public: Database() { ifstream infile(m_file_name, ios::binary); if (!infile.is_open()) { return; } // 先读取记录总数 size_t record_count; infile.read(reinterpret_cast<char*>(&record_count), sizeof(record_count)); Arreglo_Alquileres.reserve(record_count); for (size_t i = 0; i < record_count; ++i) { Alquiler alq; alq.read(infile); Arreglo_Alquileres.push_back(std::move(alq)); } infile.close(); } void add(const Alquiler &x) { Arreglo_Alquileres.push_back(x); write(); } bool write() { ofstream outfile(m_file_name, ios::binary | ios::trunc); if (!outfile.is_open()) { return false; } // 先写入记录总数 size_t record_count = Arreglo_Alquileres.size(); outfile.write(reinterpret_cast<const char*>(&record_count), sizeof(record_count)); for (const auto& alq : Arreglo_Alquileres) { alq.save(outfile); } outfile.close(); return true; } // 测试用:打印所有记录 void print_all() const { for (const auto& alq : Arreglo_Alquileres) { alq.print(); } } }; int main() { Database base; vector<string> v1 = {"panuelo", "alpargatas", "chaleco"}; Alquiler x1(9,8,7,{6,5,4},{3,2,1},v1); base.add(x1); vector<string> v2 = {"sombrero", "guantes"}; Alquiler x2(123, 45, 10, {10, 11, 2024}, {17, 11, 2024}, v2); base.add(x2); // 验证数据是否正确存储 base.print_all(); return 0; }
4. 关键修改说明
- 先写记录总数:读取时先获取记录总数,再循环读取对应数量的记录,彻底解决记录数计算错误问题
- 直接序列化成员变量:删除冗余结构体,代码逻辑更清晰,减少出错概率
- 使用
size_t替代int:处理长度和数量时使用标准无符号整数类型,避免溢出风险 - 添加测试打印函数:方便验证数据存储和读取的正确性
额外注意事项
- 跨平台兼容性:不同平台的字节序(大小端)可能存在差异,若需要跨平台支持,需添加字节序转换逻辑
- 错误处理:当前代码未处理文件读写失败的边界情况(如读取到文件末尾),实际项目中可添加
infile.good()或infile.eof()的检查,避免数据损坏
内容的提问来源于stack exchange,提问作者Agustín Saenz
相关产品推荐
相关产品推荐

