必须加 std::ios::binary 才能正确读二进制文件,否则文本模式会将 \0 视为结束、自动转换 \r\n,且 read() 遇 \0 提前终止;应检查 gcount() 而非 eof(),禁用 >> 和 getline() 等文本函数。

用 std::ifstream 读二进制文件必须加 std::ios::binary
默认的文本模式会把 \0 当作字符串结束,哪怕你用 read() 也会在遇到第一个 \0 后“看似读完”——其实只是流状态被悄悄置为 failbit,后续读取直接跳过。加 std::ios::binary 才能原样对待每个字节。
- 不加时:即使调用
read(buf, size),遇到\0不报错但提前终止,gcount()返回值远小于预期 - 加了之后:
read()严格按字节数读,\0和0xFF一样只是普通字节 - Windows 下尤其明显:文本模式还会把
\r\n自动转成\n,破坏原始二进制结构
read() 后必须检查 gcount() 和流状态
二进制读取没有“自动截断”逻辑,但系统调用可能因磁盘错误、权限或文件末尾提前返回。只靠 !ifs 或 ifs.eof() 判断非常危险——eof() 只在尝试越界后才置位,最后一次成功读取后它仍是 false。
- 正确姿势:
ifs.read(buf, len); if (ifs.gcount() != len) { /* 实际读到的比想要的少 */ } -
gcount()是唯一可信的已读字节数,failbit可能因缓冲区满或磁盘 I/O 失败而置位 - 别用
while (ifs.read(...)):第一次失败就跳出,根本没机会处理部分读取
用 std::vector<char></char> + read() 比 getline() 或 >> 安全得多
operator>> 和 getline() 都是文本语义函数,内部依赖空字符、空白符、换行符做分隔,读二进制等于主动往坑里跳。
- 永远不要对二进制数据调用
ifs >> some_var或std::getline(ifs, str) - 推荐模式:
std::vector<char> buf(size); ifs.read(buf.data(), size);</char> - 如果文件大小未知,先用
seekg(0, std::ios::end); tellg()获取长度(注意大文件和某些文件系统限制)
Windows 上用 fopen() 开二进制文件也得加 "rb"
C 风格接口同样踩坑:不加 b 标志,fread() 在 Windows 文本模式下仍会把 \r\n 转 \n,且遇到 \0 可能影响内部计数逻辑(尤其搭配 fgets() 时)。
立即学习“C++免费学习笔记(深入)”;
-
fopen("data.bin", "rb")是底线,"r"就是错的 -
fread(ptr, 1, size, fp)的返回值是实际读取字节数,必须和size对比,不能只看是否为0 - 混用 C/C++ 文件接口(比如
fopen+std::istream)会导致未定义行为,选一种风格到底
最常被忽略的是:即使开了 binary 模式,如果后续用错了读取函数(比如拿 std::getline() 去读一个 PNG 文件头),照样崩——模式只是开关,不替你选对 API。



















