用 std::fstream 读写二进制文件必须加 std::ios::binary,且需分别管理读写指针、检查实际读写字节数、校验流状态并处理平台差异。

用 std::fstream 打开二进制文件必须加 std::ios::binary
不加这个标志,Windows 下读写会把 \x0D\x0A 自动转成 \x0A(或反过来),导致二进制数据错位。Linux 虽然影响小,但跨平台代码必须统一处理。
常见错误现象:read() 返回字节数少于预期、写入后文件变大、图片/音频文件损坏。
- 打开时务必同时指定
std::ios::in | std::ios::out | std::ios::binary - 只读用
std::ios::in | std::ios::binary,只写用std::ios::out | std::ios::binary,缺一不可 -
std::fstream默认不支持追加写(app模式会强制移到末尾,无法随机写),别和std::ofstream的app混用
seekg() 和 seekp() 不是同一个指针
这是最常被忽略的点:C++ 标准规定,std::fstream 有两个独立的文件位置指针——一个管读(get),一个管写(put)。调 seekg(100) 不会影响写位置,反之亦然。
使用场景:想在某偏移处读 4 字节,再原地写回修改后的值,就必须分别调一次 seekg() 和 seekp()。
立即学习“C++免费学习笔记(深入)”;
- 读写同一位置前,先用
seekg(pos)定位读指针,再用seekp(pos)定位写指针 - 如果只调一个,另一个仍停在上次操作结束处,极易读到旧数据或覆盖错位置
- 用
tellg()/tellp()可分别检查当前读/写位置,调试时很有用
读写缓冲区大小与 read()/write() 的实际行为
read() 和 write() 不保证一次性完成请求长度,尤其在文件末尾或出错时。返回值才是真实操作字节数,不能假设“传了 1024 就一定读了 1024”。
常见错误现象:循环读取时没检查 gcount(),导致最后几字节丢掉;写入后没判断 fail(),以为数据已落盘。
- 每次
read(buf, n)后,立刻用stream.gcount()获取实际读取字节数 - 写入后检查
!stream.good()或stream.fail(),而不是只看write()返回值(它总是返回*this) - 二进制读写建议用
char*缓冲区,避免std::string中间插入空字符导致截断
文件指针移动后立即读写可能失败,需注意流状态
调用 seekg() 或 seekp() 后,如果文件位置超出当前文件大小(比如 seek 到 1MB 但文件只有 10KB),后续 write() 可能静默失败,或触发 failbit 但没人检查。
使用场景:扩展文件并填充中间空白(如预分配日志文件)、随机更新结构体数组中的某一项。
- 写入前确保位置合法:可用
seekg(0, std::ios::end)+tellg()获取当前大小,再比对目标偏移 - 若需扩展文件,先用
seekp(file_size - 1)再write("\0", 1)填充,或直接用系统调用truncate()(POSIX)或SetFilePointerEx()(Windows) - 任何指针移动后,建议紧跟
clear()清除可能残留的eofbit或failbit,否则后续操作可能被跳过
二进制文件读写真正麻烦的不是语法,而是指针状态、缓冲区边界、平台换行符、流错误位这四者的组合效应。少检查一个 gcount(),少清一次 failbit,就可能让程序在某个特定文件大小下开始出错,而且很难复现。











