核心是「内存布局即文件内容」:结构体须为标准布局且无非POD成员,用vector连续存储后一次write;含std::string等需手动序列化;写前clear()和seekp(),开binary模式;读时严格校验gcount()与eof()/fail()。

直接用 write() 一次性写结构体数组,别单个循环
结构体二进制写入的核心是「内存布局即文件内容」。只要结构体不含指针、虚函数、非POD成员,sizeof(MyStruct) 就是它在磁盘上该占的字节数。多次调用 write() 写单个结构体,不仅慢,还容易因 seekp() 错位或流状态异常导致错位。正确做法是把多个结构体放进连续内存(比如 std::vector<mystruct></mystruct>),然后一次 write()。
- 确保结构体是标准布局(
std::is_standard_layout_v<mystruct></mystruct>为true),没有std::string、std::vector等非 trivial 类型 - 用
reinterpret_cast<const char></const>转换数据起始地址,长度用vec.size() * sizeof(MyStruct) - 检查
write()返回值:若小于预期字节数,说明写入被截断(如磁盘满、权限不足)
std::vector<MyStruct> data = {/* ... */};
file.write(reinterpret_cast<const char*>(data.data()),
static_cast<std::streamsize>(data.size() * sizeof(MyStruct)));结构体里有 std::string 或动态成员?必须手动序列化
不能直接 write() 含 std::string 的结构体——它内部只存指针和长度,写进去的是野地址,读出来全是乱码或崩溃。这种结构体不是 POD,sizeof 不代表实际数据体积。
- 要么改用固定长度 C 风格字符数组(如
char name[32]),保持 trivial 可位拷贝 - 要么放弃块写入,逐字段写:先写字符串长度
uint32_t,再写字符串内容,再写其他字段 - 注意字节序一致性:跨平台时,
htonl()/ntohl()处理整数长度字段
常见错误现象:read() 出来 std::string 的 c_str() 指向非法内存,或者程序直接 segfault。
写入前必须调用 file.clear() 和 file.seekp(0, std::ios::beg)
二进制文件复用时,如果之前读过或写到中途出错,流可能处于 failbit 或 eofbit 状态,后续 write() 会静默失败。同时,文件位置指针可能停在末尾或中间,不重置就写,数据会覆盖或插入错位。
立即学习“C++免费学习笔记(深入)”;
-
file.clear()清除所有错误标志(failbit,badbit,eofbit) -
file.seekp(0, std::ios::beg)把写位置设到开头;若要追加,用std::ios::app打开,但此时不能块写入已有数据区 - 打开模式必须含
std::ios::binary,否则 Windows 下\n会被转成\r\n,破坏二进制一致性
典型错误:没清标志位,write() 返回 0,但没人检查,以为写成功了。
读取时用 read() 对应块读,且务必校验返回值
写是块写,读就必须块读,且长度严格匹配。不能假设 read() 一定读满——文件可能被截断、磁盘损坏、或写入时出错未察觉。
- 用
file.gcount()获取实际读取字节数,除以sizeof(MyStruct)得真实结构体个数 - 若
gcount() % sizeof(MyStruct) != 0,说明文件损坏或写入不完整 - 读完后检查
file.eof()或file.fail():仅eof()是正常结束;fail()表示读取失败(如权限、磁盘错误)
容易被忽略的点:很多人只检查 !file,但 read() 半途失败时 failbit 已置,而 eofbit 还没置,!file 为 true,但 gcount() 已返回部分数据——这部分数据是否可用,得你自己判断。



















