必须用"rb"/"wb"等带b模式打开二进制文件:Windows下不加b会导致换行符被错误转换,引发字节偏移错乱、结构体读取失败、媒体文件损坏;跨平台代码须统一加b;fseek偏移量单位为字节;fread返回元素个数而非字节数;C++中std::ifstream需显式指定ios::binary。

用 fopen 以二进制方式打开文件,"rb" 和 "wb" 到底怎么选
开文件不加 b 后缀,在 Windows 下读写二进制数据大概率出错——换行符会被悄悄转换成 \r\n 或反过来,导致字节偏移错乱、结构体读取失败、图片/音频头损坏。
必须显式使用 "rb"(读)、"wb"(写)、"r+b"(读写)等带 b 的模式。Linux/macOS 虽然对文本/二进制模式不敏感,但跨平台代码必须统一用 b,否则一到 Windows 就崩。
-
"r"→ 改成"rb";"w"→ 改成"wb";想读写已有文件用"r+b",别用"w+b"(会清空文件) - Windows 下用
"rt"或"wt"才触发换行转换;C++ 标准库的std::ifstream默认就是二进制语义,但底层仍依赖fopen时,模式字符串不能偷懒 - 如果用
fread/fwrite读写原始内存块(比如struct或std::vector<char></char>),b是硬性前提
fseek 定位文件指针:偏移量单位是字节,不是字符或元素
二进制文件里没有“字符”概念,fseek 的第三个参数是起始点(SEEK_SET/SEEK_CUR/SEEK_END),第二个参数是**字节偏移量**——传 sizeof(int) * i 才能跳到第 i 个 int,传 i 就直接错位。
- 定位到文件开头:
fseek(fp, 0, SEEK_SET);末尾前一个字节:fseek(fp, -1, SEEK_END)(注意负偏移只对SEEK_END安全) - 用
ftell获取当前位置时,返回值是long,大文件(>2GB)可能溢出;64 位系统建议用_fseeki64+_ftelli64(Windows)或fseeko/ftello(POSIX) - 在
"r+b"模式下修改某段数据后,必须调用fseek或fflush才能切换读/写状态,否则行为未定义(glibc 会报Invalid seek)
读取时 fread 返回值不等于请求字节数?别急着报错
fread 返回的是**成功读取的元素个数**(第一个参数是元素大小,第二个是元素数量),不是字节数。常见误判是把 fread(buf, 1, 100, fp) 的返回值和 100 比较——它其实返回的是读了几个字节(因为每个元素大小为 1),但逻辑上容易绕晕。
立即学习“C++免费学习笔记(深入)”;
- 正确检查是否读满:
size_t n = fread(buf, 1, 100, fp); if (n != 100) { /* 可能到 EOF 或出错 */ } - 如果用
fread(buf, sizeof(MyStruct), count, fp),返回值是成功读入的MyStruct个数,不是字节数——别拿它和count * sizeof(MyStruct)比 - 读到 EOF 不算错误,
ferror(fp)才判断真实错误;feof(fp)只在尝试读越界后才置位,不能提前用来判断“还有没有数据”
用 std::ifstream 时,ios::binary 必须显式设置
C++ 流默认是文本模式,即使你用 read() 读原始字节,Windows 下仍可能触发 C 运行时的换行转换——除非你明确告诉它:“这是二进制”。ios::binary 不是可选项,是保命开关。
- 构造时就要带上:
std::ifstream fin("data.bin", std::ios::binary);;后续再fin.open(...)也得传同样标志 - 用
fin.seekg(100)定位,偏移单位仍是字节;但要注意seekg在文本模式下对 Unicode 文件行为未定义,二进制模式才可靠 - 如果混用
operator>>(格式化输入)和read()(原始读取),必须调用fin.clear()清除流状态位,否则格式化操作残留的失败标志会影响后续read()
二进制 I/O 的坑不在语法,而在对“字节即一切”的敬畏——少一次 b,少一次 sizeof,少一次 clear(),都可能让程序在某个环境里静默错乱。











