用std::ofstream以std::ios::app模式打开才能追加;默认构造会清空文件,显式传入std::ios::app后所有写入均从文件末尾开始,seekp()无效。

用 std::ofstream 以 std::ios::app 模式打开就能追加
不是所有打开方式都支持追加,关键在构造时传对标志位。默认的 std::ofstream 构造会清空文件,必须显式加上 std::ios::app —— 它强制所有写入都从文件末尾开始,哪怕你调用了 seekp() 也没用。
常见错误现象:ofstream("log.txt") 看似简单,结果旧内容全没了;或者用 <code>std::ios::out | std::ios::ate,以为“定位到末尾”就等于追加,其实只是把读写位置设在末尾,后续 write() 或 仍可能覆盖原有字节。
- 正确写法:
std::ofstream file("log.txt", std::ios::app); - 如果还要读取(比如先读再追加),得用
std::fstream并同时带std::ios::app | std::ios::out,但注意:此时in标志无效,app会压制读行为 -
std::ios::app自动隐含std::ios::out,不用重复写
追加时中文乱码或换行异常?检查流的 locale 和缓冲
Windows 下用记事本打开追加后的文件发现中文变方块、换行变成 ^M,大概率是编码或换行符没对齐。C++ 标准库不处理 UTF-8 BOM,也不自动转换 \n 为 \r\n —— 这些都得自己管。
使用场景:日志系统、配置生成、临时数据拼接。一旦跨平台(比如 Linux 写的文件在 Windows 打开),问题立刻暴露。
立即学习“C++免费学习笔记(深入)”;
- 写中文前,显式设置 locale:
file.imbue(std::locale("")); // 依赖系统环境,或更稳妥地用std::locale::global(std::locale("zh_CN.UTF-8"))(Linux) - 避免依赖系统默认换行:手动写
"\r\n"而非只用(后者会 flush,影响性能且换行符不可控) - 追加大量小段内容时,
std::endl频繁刷盘很慢,改用"\n"+ 最后一次file.flush()
多个线程同时追加?std::ofstream 不是线程安全的
别指望两个线程各自开一个 std::ofstream("log.txt", std::ios::app) 就能安全并发写——操作系统层面的 write() 调用虽是原子的(对短内容),但 C++ 流对象内部的格式化缓冲、指针维护、异常状态等完全没加锁。
典型错误现象:日志行错位、半截字符串、某次写入彻底消失。尤其在高频率写入时,概率飙升。
- 最简方案:用一个全局
std::ofstream对象 +std::mutex保护每次操作 - 更高效的做法:用无锁队列收集日志字符串,单个 writer 线程批量写入(避免频繁磁盘 I/O)
- 不要尝试用
freopen()+fprintf()替代——C 风格 FILE* 同样不保证多线程追加安全
追加失败却没报错?检查 failbit 和文件权限
file 看似执行了,但实际可能什么都没写进去。原因常被忽略:文件系统满、目录无写权限、文件被其他进程独占锁定(如 Windows 上用记事本打开未关闭)。
性能影响:不检查状态就继续往下跑,可能导致后续逻辑基于“已写入”的错误假设,调试时极难定位。
- 每次写完立刻检查:
if (!file) { /* 处理错误 */ },而不是只在打开时检查 - 用
file.clear()清除错误标志前,先用file.rdstate()判断是failbit还是badbit(后者通常意味着底层 I/O 错误,不可恢复) - Linux 下可用
strace -e trace=write,openat ./your_program直接看系统调用是否失败
事情说清了就结束。追加看着简单,但乱码、并发、静默失败这三类问题,八成出在没盯住标志位、locale 和状态检查上。











