C++读写二进制文件时类内std::string对象读取为空问题求助
问题根源
你当前直接将类对象的原生内存整体写入二进制文件的操作,仅适用于POD(普通旧数据)类型的类。std::string不属于POD类型,它内部仅存储指向堆内存的指针、字符串长度、容量等元信息,实际的字符串内容存储在堆内存中。你写入文件的只是string对象本身的元数据,没有写入实际的字符串内容,读回时拿到的指针指向的是无效的堆内存地址,自然读不到数据,甚至可能触发程序崩溃。
修复方案
你需要手动实现类的序列化(对象转二进制数据写入文件)和反序列化(二进制数据读入重构对象)逻辑,不要直接读写整个对象的内存。
步骤1:给User类添加序列化/反序列化接口
修改类的public部分,新增两个方法:
class User{ private: unsigned int user_id; std::string user_name, home_address, e_mail; char gender, date_of_birth[11]; unsigned long long int phone; bool has_car_booked, is_admin; void validate_data(); public: void get_data(bool); void display(); // 新增序列化、反序列化方法 void serialize(std::ofstream& ofs) const; void deserialize(std::ifstream& ifs); };
步骤2:实现序列化逻辑
写文件时先写固定长度的POD成员,再处理string类型:先写入string的长度,再写入实际的字符串内容。
void User::serialize(std::ofstream& ofs) const { // 写入基础POD类型 ofs.write(reinterpret_cast<const char*>(&user_id), sizeof(user_id)); ofs.write(&gender, sizeof(gender)); ofs.write(date_of_birth, sizeof(date_of_birth)); ofs.write(reinterpret_cast<const char*>(&phone), sizeof(phone)); ofs.write(reinterpret_cast<const char*>(&has_car_booked), sizeof(has_car_booked)); ofs.write(reinterpret_cast<const char*>(&is_admin), sizeof(is_admin)); // 处理std::string:先写长度,再写内容 size_t len = user_name.size(); ofs.write(reinterpret_cast<const char*>(&len), sizeof(len)); ofs.write(user_name.data(), len); len = home_address.size(); ofs.write(reinterpret_cast<const char*>(&len), sizeof(len)); ofs.write(home_address.data(), len); len = e_mail.size(); ofs.write(reinterpret_cast<const char*>(&len), sizeof(len)); ofs.write(e_mail.data(), len); }
步骤3:实现反序列化逻辑
读文件时和序列化顺序严格对应,读string时先读长度,再读取对应长度的内容赋值给string:
void User::deserialize(std::ifstream& ifs) { // 读取基础POD类型 ifs.read(reinterpret_cast<char*>(&user_id), sizeof(user_id)); ifs.read(&gender, sizeof(gender)); ifs.read(date_of_birth, sizeof(date_of_birth)); ifs.read(reinterpret_cast<char*>(&phone), sizeof(phone)); ifs.read(reinterpret_cast<char*>(&has_car_booked), sizeof(has_car_booked)); ifs.read(reinterpret_cast<char*>(&is_admin), sizeof(is_admin)); // 处理std::string:先读长度,再读内容 size_t len; ifs.read(reinterpret_cast<char*>(&len), sizeof(len)); user_name.resize(len); ifs.read(user_name.data(), len); ifs.read(reinterpret_cast<char*>(&len), sizeof(len)); home_address.resize(len); ifs.read(home_address.data(), len); ifs.read(reinterpret_cast<char*>(&len), sizeof(len)); e_mail.resize(len); ifs.read(e_mail.data(), len); }
步骤4:修改读写文件的函数
不要直接读写整个对象内存,改为调用上面实现的序列化/反序列化方法:
void read_from_file(const std::string &filename){ std::ifstream file; file.open(filename, std::ios::binary | std::ios::in); if(!file){ std::cout<<"Read File does not exist\n"; return; } User tempobj; tempobj.deserialize(file); // 调用反序列化方法 tempobj.display(); file.close(); } void write_to_file(const User &obj, const std::string &filename){ std::ofstream file; file.open(filename, std::ios::binary | std::ios::out); obj.serialize(file); // 调用序列化方法 file.close(); }
注意事项
- 序列化和反序列化的字段顺序必须严格一致,否则会出现数据读错的问题
- 如果需要跨平台运行,还要额外处理大小端、不同平台基础类型长度差异、结构体字节对齐的问题,本地使用的话上述代码已经可以满足需求。
内容的提问来源于stack exchange,提问作者Dev Narula
相关产品推荐
相关产品推荐

