用 std::ifstream 读二进制文件必须指定 std::ios::binary 模式,否则 Windows 会误转 \r\n;需检查 is_open()、用 gcount() 获取实际读取字节数、强制转 char*、注意结构体 POD 性与内存对齐、结合 eof()/fail() 判断状态、优先用 std::vector 管理缓冲区。

用 std::ifstream 以二进制模式读取文件,必须加 std::ios::binary
不加这个标志,Windows 下会把 \r\n 自动转成 \n,导致读出的字节数变少、内容错位。Linux 虽然影响小,但跨平台代码必须统一处理。
常见错误是只写 std::ifstream f("data.bin"),没指定模式——这默认是文本模式,对二进制数据完全不可靠。
-
std::ifstream f("data.bin", std::ios::binary | std::ios::in)是安全写法 - 读之前建议先检查文件是否打开成功:
if (!f.is_open()) { /* error */ } - 用
f.gcount()获取上一次read()真实读取的字节数,不能只信read()的返回值(它返回的是流对象本身)
read() 和 write() 的参数必须是 char* 类型指针
C++ 文件流的 read() 和 write() 不接受任意类型指针,哪怕你传 int* 或 struct MyData*,也得强制转成 char*,否则编译失败。
例如想读一个 float:
立即学习“C++免费学习笔记(深入)”;
float x; fin.read(reinterpret_cast(&x), sizeof(x));
注意两点:
- 必须用
reinterpret_cast,C 风格的(char*)&x虽能过,但不推荐 -
sizeof(x)必须准确,不能写成sizeof(float)如果x是const float&等可能退化的情况 - 结构体直接读写前,要确认它没有虚函数、没有非 POD 成员,且最好用
#pragma pack(1)避免对齐填充干扰
读完记得检查 eof() 和 fail(),别只靠 gcount()
比如读固定大小块时,最后一块可能不足预期长度,gcount() 返回实际字节数,但流状态可能已经是 eofbit;而如果磁盘满或权限出问题,failbit 会被置位,gcount() 却可能还是非零。
典型误判写法:while (fin.read(buf, 1024)) { ... } —— 这个循环条件其实永远为真(read() 返回引用),根本不会退出。
- 正确做法是:每次
read()后立刻检查if (fin.gcount() == 0) break; - 完整健壮判断:
if (fin.fail() && !fin.eof()) { /* I/O error */ } - 读到末尾但没出错时,
eof()为 true,fail()为 false,gcount()可能小于请求长度
用 std::vector 管理缓冲区比裸 new char[] 更安全
手动 new[] 分配缓冲区容易忘 delete[],尤其在异常路径下;而且尺寸计算稍错就可能越界写入。
std::vector 自动管理内存,还能直接取 .data() 给 read() 用:
std::vectorbuf(4096); fin.read(buf.data(), buf.size());
- 不需要
resize()后再data(),vector构造时已分配连续内存 - 如果后续要 reinterpret 为其他类型(如
int32_t*),确保buf.size()是目标类型的整数倍,且起始地址满足对齐要求(alignof(int32_t)) - 大文件一次性读入?小心内存爆掉——优先考虑分块读,或用
mmap(POSIX)/CreateFileMapping(Windows)
二进制 I/O 最容易被忽略的其实是字节序和结构体内存布局,这两点不显式处理,换平台或换编译器就可能读出垃圾数据。










