c++oding="utf-8" ?>
用 std::ios::in | std::ios::out 打开不存在的文件会静默失败,因标准不自动创建;需加 std::ios::trunc 或先确保文件存在;读写切换前必须调用 seekg()/seekp() 同步位置,且应使用二进制模式。

fstream 用 std::ios::in | std::ios::out 打开文件会失败?
直接用 std::fstream 同时指定 std::ios::in 和 std::ios::out 打开一个**尚不存在的文件**,默认会失败(is_open() 返回 false),因为 C++ 标准要求:以 out 模式打开时,若文件不存在,默认行为是创建它;但加上 in 后,语义变成“既要读又要写已有内容”,标准库不自动创建,也不报错,只是静默失败。
解决方法是显式加上 std::ios::trunc 或 std::ios::app,或者先确保文件存在:
-
std::ios::trunc:文件存在则清空,不存在则创建 —— 适合覆盖写场景 -
std::ios::app:强制追加,所有写入都发生在末尾,且seekp()无效 —— 不适合随机读写 - 更稳妥的做法:先用
std::ofstream创建空文件(或检查存在性),再用std::fstream以in|out打开
读写同一个文件时,seekg() 和 seekp() 必须同步吗?
是的,绝大多数实现(包括 libstdc++ 和 MSVC)要求:在同一个 std::fstream 对象中进行读写切换前,必须调用 seekg() 或 seekp()(哪怕位置不变),否则后续读/写行为未定义 —— 常见表现是读到旧缓存、写入被跳过、或 failbit 被置位。
原因在于底层流缓冲区维护着独立的读位置和写位置指针,切换方向时需刷新并重置缓冲状态。正确做法:
- 写完后想读:先调用
seekg(0, std::ios::cur)或明确跳转,再读 - 读完后想写:先调用
seekp(0, std::ios::cur)或明确跳转,再写 - 不要依赖“刚写完的位置就是下次读的起点”——这不可靠
用 std::fstream 随机读写二进制文件的典型模式
文本模式下换行符处理、缓冲策略会导致位置计算失准,所以涉及位置跳转的读写务必用二进制模式:std::ios::binary。常见结构如下:
std::fstream file("data.bin", std::ios::in | std::ios::out | std::ios::binary);
if (!file.is_open()) { /* 处理错误 */ }
<p>// 写入结构体
MyStruct s = {123, 45.6};
file.write(reinterpret_cast<const char>(&s), sizeof(s));</const></p><p>// 切换到开头读取
file.seekg(0); // 必须!
MyStruct loaded;
file.read(reinterpret_cast<char>(&loaded), sizeof(loaded));
</char></p>
注意点:
-
write()和read()的字节数必须严格匹配,不能依赖sizeof(std::string)等非 POD 类型 - 结构体需是 trivially copyable,无虚函数、无非 POD 成员
- 每次
read()后检查file.gcount()或!file,防止短读
为什么 std::fstream 在 Windows 上读写同一文件容易出错?
Windows 默认使用 CRLF(\r\n)换行,而二进制模式虽绕过换行转换,但某些旧 CRT 实现(尤其 MinGW)仍会在 in|out 模式下对缓冲区做额外校验,导致 seekg() 后 read() 返回 0 字节。
规避方式:
- 始终启用
std::ios::binary,哪怕内容是文本 - 避免混合使用
/<code>>>(格式化 I/O)和read()/write()(原始 I/O) - 如需跨平台稳定行为,优先用
std::ofstream+std::ifstream分开操作,或改用内存映射(mmap/CreateFileMapping)
最易被忽略的是:即使你没写错代码,不同编译器对“读写切换”的缓冲刷新时机定义不完全一致,所以只要涉及位置跳转,就老老实实加一次 seekg() 或 seekp()。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











