二进制文件读写必须显式指定std::ios::binary标志,否则换行符转换和eof截断会导致数据静默损坏;需用read()/write()配对操作原始内存,注意结构体布局、类型宽度及错误处理。

用 std::ifstream 和 std::ofstream 以二进制模式打开文件
默认的文本模式会做换行符转换(比如 \n → \r\n)和 EOF 截断,读写二进制数据时必须显式指定 std::ios::binary。漏掉这个标志,文件内容大概率被悄悄破坏。
常见错误现象:read() 返回字节数少于预期、图片/音频文件打不开、结构体反序列化后字段值错乱。
- 打开输入文件:用
std::ifstream fin("data.bin", std::ios::binary),不要省略第二个参数 - 打开输出文件:用
std::ofstream fout("out.bin", std::ios::binary),如果要追加写,加上| std::ios::app - 检查是否成功:直接判断流对象(如
if (!fin)),别只看is_open()—— 权限不足或路径错误时它可能返回 true 但后续操作失败
read() 和 write() 必须配对使用原始内存块
这两个函数不认类型,只按字节搬运内存。传给 read() 的缓冲区地址必须是 char* 或 unsigned char*,长度单位是字节,不是元素个数。
使用场景:读取图像头、解析协议包、保存/加载结构体快照。
立即学习“C++免费学习笔记(深入)”;
- 正确写法:
fin.read(reinterpret_cast<char>(&header), sizeof(header))</char>——sizeof是关键,不能写成sizeof(&header) - 容易踩的坑:对
std::string或std::vector直接write(),它们内部指针指向堆内存,写进去的是垃圾地址;应先data()+size() - 性能影响:一次
read()多少字节有讲究。太小(如每次 1 字节)系统调用开销大;太大(如 100MB)可能吃光栈或触发异常;建议 4KB–64KB 区间分块读
结构体序列化前得确认内存布局是否“标准”
直接 write() 一个结构体,只有在它满足 std::is_standard_layout_v<t></t> 且不含虚函数、引用、非 POD 成员时才安全。否则成员之间可能有编译器插入的填充字节,或者顺序被打乱。
错误现象:同一段代码在不同编译器(MSVC / GCC / Clang)或不同优化等级下读出的数据不一致。
- 检查方法:用
static_assert(std::is_standard_layout_v<mystruct>)</mystruct>编译期断言 - 更稳妥做法:手动逐字段
write(),或用#pragma pack(1)强制紧凑布局(但要注意平台字节序和对齐要求) - 兼容性陷阱:
int在 32 位和 64 位系统上大小可能不同;跨平台存数据时优先用int32_t、uint64_t等固定宽度类型
关闭文件前记得 clear() 流状态再 flush()
二进制写入中途出错(比如磁盘满),流会进入 failbit 状态。此时即使调用 close(),底层文件句柄也可能没真正释放,或最后几 KB 数据没刷到磁盘。
真实场景:程序崩溃前最后一帧录像没保存、日志文件结尾截断。
- 安全写法:
fout.clear(); fout.flush(); fout.close();——clear()重置错误标志,否则flush()可能直接返回失败 - 别依赖 RAII 自动析构:局部
ofstream对象离开作用域时会自动close(),但如果之前已出错,这个close()不保证数据落盘 - 极端情况:用
fsync()(Linux/macOS)或FlushFileBuffers()(Windows)强制刷盘,但这需要从流获取原文件描述符(fileno(fout.rdbuf()->fd())),属于非标准扩展,慎用
二进制 IO 的麻烦不在语法,而在每个环节都依赖你对内存、ABI、系统调用行为的精确控制。哪怕只是多了一个空格、少了一个 reinterpret_cast,结果都可能是静默损坏。









