必须使用ios::binary标志读写二进制文件,正确检查gcount()和fail(),控制结构体填充并统一字节序,否则数据会损坏。
read() 和 write() 读写二进制文件必须用 ios::binary
不加这个标志,read() 和 write() 在 Windows 下会把 \x0A(LF)和 \x0D\x0A(CRLF)互相转换,导致二进制数据损坏。Linux/macOS 虽然影响小,但行为不一致,必须统一加。
std::ifstream 和 std::ofstream 构造时传入 std::ios::binary
open() 后再调用 setf(std::ios::binary) —— 这个调用无效std::fstream 并带上 ios::in | ios::out | ios::binary
read() 和 write() 的参数是 char* 和字节数,不是对象引用它们不识别类型,只按字节搬运。想写一个 int 或结构体,必须取地址并转成 char*,且确保内存布局安全(比如没虚函数、没非 POD 成员)。
int x = 42:file.write(reinterpret_cast(&x), sizeof(x))
file.read(reinterpret_cast(&x), sizeof(x))
std::is_trivially_copyable_v 为 true(大多数纯数据 struct 满足)std::string 或 std::vector 本体——它们内部指针不会被序列化gcount() 和 fail(),不能只看 eof()
read() 可能因文件末尾、磁盘错误或权限问题提前终止,gcount() 返回**实际读取字节数**,它可能小于你请求的长度;fail() 在出错后才置位,eof( 只表示上次操作碰到了结尾,不是当前状态。
)
if (!file) { /* 写失败 */ }
while (file.read(buf, sizeof(buf))) {
size_t n = file.gcount();
// 处理 n 字节
}
if (file.fail() && !file.eof()) {
// 真正出错了
}while (!file.eof()) 控制读循环——它会导致多读一次失败即使你正确用了 ios::binary,在 x86(小端)和 ARM(可能大端)之间传输文件,或者不同编译器对同一 struct 的 padding 不同,都会让读出来的值错乱。
int32_t、uint16_t 可减少歧义,但仍需约定字节序(推荐网络序:用 htons()/htonl() 写,ntohs()/ntohl() 读)#pragma pack(1) 强制紧凑排列(但注意性能损失和对齐访问异常风险)二进制 I/O 表面简单,但 ios::binary 忘加、gcount() 不查、结构体 padding 不控、字节序不处理——这四点踩中任意一个,文件就可能无声无息地损坏。