You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:42:51