C++如何读写包含std::string、std::vector的变长结构体到二进制文件
问题根本原因
你遇到的问题本质是std::string、std::vector这类动态容器的内存布局不是连续存储实际内容:结构体里只保存了指向堆内存的指针、长度等元数据,直接fwrite结构体只会把指针地址、固定大小的元数据写入文件,实际的字符串内容、vector存储的数值都不会被写入,读取回来的指针指向的是无效内存,自然无法正常使用。
可行实现方案
方案1:手动实现序列化(无第三方依赖,可控性最高)
你可以按照固定的自定义格式逐字段写入/读取,不需要依赖任何外部库,适合轻量场景:
写入逻辑(单条entry为例)
// 写入单个entry到文件 void write_entry(FILE* fp, const entry& e) { // 1. 写入固定大小的id fwrite(&e.id, sizeof(uint64_t), 1, fp); // 2. 写入name的长度,再写入name的实际内容 size_t name_len = e.name.size(); fwrite(&name_len, sizeof(size_t), 1, fp); fwrite(e.name.data(), sizeof(char), name_len, fp); // 3. 写入data的长度,再写入data的所有元素 size_t data_len = e.data.size(); fwrite(&data_len, sizeof(size_t), 1, fp); fwrite(e.data.data(), sizeof(uint64_t), data_len, fp); } // 写入整个vector void write_entry_vector(FILE* fp, const std::vector<entry>& vec) { // 可选:先写入总条目数,读取的时候可以先读总大小提前分配vector空间 size_t total = vec.size(); fwrite(&total, sizeof(size_t), 1, fp); for (const auto& e : vec) { write_entry(fp, e); } }
读取逻辑(单条entry为例)
// 从文件读取单个entry void read_entry(FILE* fp, entry& e) { // 1. 读取id fread(&e.id, sizeof(uint64_t), 1, fp); // 2. 读取name长度,再读取内容赋值给string size_t name_len = 0; fread(&name_len, sizeof(size_t), 1, fp); e.name.resize(name_len); fread(e.name.data(), sizeof(char), name_len, fp); // 3. 读取data长度,再读取所有元素赋值给vector size_t data_len = 0; fread(&data_len, sizeof(size_t), 1, fp); e.data.resize(data_len); fread(e.data.data(), sizeof(uint64_t), data_len, fp); } // 读取整个vector void read_entry_vector(FILE* fp, std::vector<entry>& vec) { size_t total = 0; fread(&total, sizeof(size_t), 1, fp); vec.resize(total); for (size_t i = 0; i < total; i++) { read_entry(fp, vec[i]); } }
注意事项
- 如果你的文件需要跨平台使用,要统一处理字节序:写入时把数值转成大端(网络字节序),读取时转成本地字节序,避免不同CPU架构的字节序差异导致数据错乱。
- 如果后续结构体可能迭代修改,建议在文件最开头写入版本号,不同版本对应不同的读写逻辑,保证向前兼容。
方案2:使用成熟的序列化库(适合复杂场景)
如果你的数据结构后续会频繁迭代,或者需要支持跨语言读写,可以直接用现成的序列化框架,不需要自己处理字节序、字段兼容问题:
- Protobuf:通用型强,压缩率和性能均衡,需要先写.proto结构定义文件生成序列化代码
- FlatBuffers:适合性能要求极高的场景,不需要 unpack 就可以直接读取数据
- nlohmann/json:可以序列化为BSON等二进制格式,不需要额外写定义,使用简单但性能相对低一些
核心原则
序列化的核心逻辑是:所有动态长度的字段,都要先写长度再写实际内容,读写顺序严格保持一致,只要遵守这个原则,不管是手动实现还是用第三方库,都不会出现数据错乱的问题。
内容的提问来源于stack exchange,提问作者Mutaru
相关产品推荐
相关产品推荐

