
用 std::ifstream 和 std::ofstream 以二进制模式打开文件
不加标志位直接打开,C++ 默认是文本模式,换行符会被悄悄转换(比如 \r\n → \n),读写二进制数据时会出错。必须显式指定 std::ios::binary。
常见错误现象:read() 返回字节数少于预期、解包结构体后字段值错乱、图片/音频文件打不开。
std::ifstream fin("data.bin", std::ios::binary);-
std::ofstream fout("out.bin", std::ios::binary | std::ios::trunc);(trunc防止追加写入) - 别用
operator>>或getline()—— 它们是为文本设计的,会停在\0或换行符,直接截断二进制流
read() 和 write() 的缓冲区与大小必须严格匹配
这两个函数不识别类型,只按字节搬运。传入的缓冲区地址和字节数稍有偏差,就会越界或漏读——尤其在处理自定义结构体时。
使用场景:保存/加载序列化结构体、读取 raw 图像帧、解析协议包头。
立即学习“C++免费学习笔记(深入)”;
- 确保
sizeof(T)真实等于你期望的字节数;有 padding 的结构体不能直接write(),需手动序列化 - 检查
gcount():调用read(buf, n)后,用fin.gcount()确认实际读了多少字节,不是靠eof()判断结束 - 示例:
char buf[1024]; fin.read(buf, sizeof(buf)); size_t n = fin.gcount();——n才是真实读取长度
跨平台读写结构体前,必须处理字节序和内存对齐
直接 write(&s, sizeof(s)) 在 x86 和 ARM 上可能读出来字段全乱,不是因为文件损坏,而是大小端不一致,或者结构体在不同编译器下 padding 不同。
性能影响:手动序列化略慢,但换来的是可移植性;盲目二进制 dump 看似快,后期调试成本极高。
- 网络字节序统一用
htons()/htonl()转整数字段,再write() - 禁用结构体自动对齐:
#pragma pack(1)或[[gnu::packed]],但仅限明确控制布局的场景 - 更稳妥的做法:用
std::memcpy按字段逐个拷贝到连续 buffer,自己控制顺序和字节宽度
读到文件末尾时,eof() 不是读操作的触发条件
很多代码写成 while (!fin.eof()) { fin.read(...); },结果最后一块数据被读两次——因为 eof() 只在尝试读超尾后才置位,不是“即将到尾”的预告。
正确逻辑永远是:先 read(),再检查是否成功,最后根据 gcount() 或 fail() 决定是否继续。
- 错误写法:
while (!fin.eof()) { fin.read(buf, 100); ... } - 正确写法:
while (fin.read(buf, 100)) { /* 处理 buf */ }或fin.read(buf, 100); if (fin.gcount() > 0) { ... } -
fail()和bad()要区分:fail()包含读不到足够字节(正常 EOF),bad()才是底层 I/O 错误(如磁盘故障)
二进制 I/O 的坑不在语法,而在“看起来读完了”和“真的读完了”之间那几字节的差值——每次少读一个字节,整个结构体就偏移失效。动手前先用 hexdump -C 对比原始文件和你写入的内容,比查文档更快定位问题。










