用 std::ofstream 以 ios::binary | ios::app 模式打开可安全追加二进制数据,前者禁用换行转换,后者确保每次写入前自动定位到文件末尾并提供原子级追加保障,但需配合文件锁或互斥量防范并发写入风险。

用 std::ofstream 以 ios::binary | ios::app 模式打开即可追加
二进制追加写入的本质,是让文件指针始终落在末尾,且不触发文本换行转换。C++ 标准库完全支持,关键在打开模式——必须同时指定 ios::binary(禁用换行符映射)和 ios::app(强制每次写入前 seek 到末尾)。
常见错误是只写 ios::ate 或手动 seekp(0, ios::end):前者只是初始定位,后续写入仍从当前位置开始,中间若有人修改文件会导致覆盖;后者在多线程/多进程场景下存在竞态,ios::app 才是原子级追加保障。
-
ios::app会忽略所有中间的seekp调用,每次write()前自动跳转到当前文件末尾 - 务必搭配
ios::binary,否则 Windows 下\n可能被悄悄转成\r\n,破坏二进制数据完整性 - 不要用
ios::out | ios::ate替代,它不保证追加语义,写入位置取决于上次操作后的指针
写入前检查文件是否可写、是否存在竞争写入
追加看似简单,但生产环境里最常崩在权限和并发上。比如多个进程同时用 ios::app 写同一个文件,C++ 标准不保证写入原子性——如果两个进程同时写入 1KB 数据,最终文件可能混杂字节,甚至损坏结构。
典型现象:二进制日志解析失败、头信息错位、校验和不匹配,但单独看每个写入块又是合法的。
- Linux/macOS 下可用
flock()加文件锁,Windows 用LockFileEx();C++ 标准库不提供跨平台文件锁,别指望ofstream自带 - 写入前用
access(path.c_str(), W_OK)(POSIX)或GetFileAttributes()(Windows)确认可写,避免静默失败 - 如果只是单进程内多线程写,用
std::mutex保护write()调用即可,不必锁整个文件
write() 的参数必须是 char* 和字节数,不能传结构体地址直接写
很多人想把 struct 直接 write(&s, sizeof(s)) 追加进去,这在内存布局固定、无虚函数、无指针成员时“可能”工作,但极易翻车。
问题不在追加逻辑本身,而在于结构体的二进制表示不可移植:编译器对齐填充、大小端、成员重排都可能导致读出乱码,尤其跨平台或升级编译器后。
- 永远显式序列化:用
memcpy拷贝字段到std::vector<char></char>,或手写serialize()成员函数 - 如果必须写原始内存,确保 struct 用
[[gnu::packed]](GCC/Clang)或#pragma pack(1)(MSVC)消除填充,并在两端统一字节序 -
write()第二个参数是std::streamsize,不是size_t,大文件写入时注意截断风险(虽然实际中极少超LLONG_MAX)
追加后不刷新,数据可能卡在缓冲区没落盘
ofstream 默认带缓冲,ios::app 模式下写入调用返回成功,不代表数据已写入磁盘。断电、崩溃或进程被 kill 时,最后几 KB 就丢了。
典型表现:程序正常退出但文件末尾少一段;用 hexdump 查看文件长度与预期不符;另一进程读取时发现“新数据还没出现”。
- 关键数据追加后立刻调用
flush(),强制刷缓冲区 - 需要更高可靠性?用
fsync()(POSIX)或FlushFileBuffers()(Windows)同步到底层存储,但性能代价明显 - 不要依赖析构函数自动 flush——异常提前退出或
exit()会绕过它
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











