必须加 std::ios::binary 标志,否则 Windows 下会自动转换换行符导致二进制数据错乱;读写须用 read()/write() 而非 >>/

用 std::ifstream 和 std::ofstream 打开文件时必须加 std::ios::binary
不加这个标志,Windows 下会把 \n 自动转成 \r\n(写入)或反过来(读取),导致二进制数据错乱。Linux/macOS 虽然影响小些,但跨平台代码必须统一处理。
常见错误是只写 std::ios::in 或 std::ios::out,漏掉 binary:
// ❌ 错误:文本模式,可能损坏二进制数据
std::ofstream file("data.bin", std::ios::out);
// ✅ 正确:显式声明二进制模式
std::ofstream file("data.bin", std::ios::out | std::ios::binary);
std::ifstream file("data.bin", std::ios::in | std::ios::binary);
读写原始内存块用 read() 和 write(),别用 >> 或
>> 和 是格式化 I/O,会解析空格、换行、类型分隔符,完全不适用于二进制数据。必须用底层字节操作函数。
-
write()第一个参数是const char*(或const void*),第二个是字节数;传sizeof(obj)时注意对象不能含指针或虚函数表 -
read()同理,但需确保目标缓冲区已分配足够空间,且调用后检查gcount()确认实际读取字节数 - 对结构体直接读写前,确认它满足
std::is_trivially_copyable_v<t></t>,否则行为未定义
示例:
立即学习“C++免费学习笔记(深入)”;
struct Header { uint32_t magic; uint16_t version; };
Header h = {0x464C457F, 1};
file.write(reinterpret_cast<const char*>(&h), sizeof(h));
Header loaded;
file.read(reinterpret_cast<char*>(&loaded), sizeof(loaded));
if (file.gcount() != sizeof(loaded)) {
// 读取不完整,可能是文件损坏或提前 EOF
}
注意字节序和结构体填充,跨平台二进制文件要自己处理
C++ 不保证结构体成员间无填充,也不规定字节序。直接 write() 一个 struct 在 x86 和 ARM 上可能读出来不一样。
- 用
#pragma pack(1)或[[gnu::packed]]消除填充(但会影响性能和对齐要求) - 整数字段建议用固定宽度类型(
uint32_t而非int),并手动做网络序/主机序转换(如htons(),ntohl()) - 如果只是本机存取、不跨平台,填充和字节序可忽略;但只要涉及文件交换、版本升级,就必须显式序列化
关闭文件前务必检查 fail() 和 bad(),尤其写入后
二进制写入失败常无声无息——比如磁盘满、权限不足,write() 不报异常,但后续 fail() 会返回 true。不检查就关文件,可能以为保存成功,实则数据丢失。
-
fail()表示格式错误或流状态异常(如写入失败) -
bad()表示底层 I/O 错误(如磁盘故障) -
good()是安全的反向判断,但更推荐显式检查!file或file.fail()
典型写法:
file.write(...);
file.close();
if (file.fail()) {
// 处理写入失败:日志、清理、抛异常等
}
二进制文件读写看着简单,真正踩坑的地方都在细节:模式标志漏写、结构体没对齐、跨平台字节序混乱、错误状态不检查。这些点任何一个出问题,都会导致数据静默损坏,比文本解析报错更难定位。


















