fail() 表示逻辑错误(如类型不匹配),bad() 表示底层I/O损坏(如磁盘满);实际业务通常只需检查fail(),而bad()为真时流基本不可恢复。

如何用 fail() 和 bad() 区分两类文件流错误
文件流出错时,fail() 和 bad() 都可能为 true,但含义不同:前者表示逻辑失败(如读取非数字到 int),后者表示底层 I/O 损坏(如磁盘满、设备断开)。实际中多数业务只需关心 fail() —— 它覆盖了文件不存在、权限不足、格式不匹配等常见问题。
检查顺序建议始终是:if (stream.fail()) 优先于 if (!stream)(二者等价),且不要仅靠 good() 判断成功,因为它在 EOF 时也返回 false,而 EOF 不一定是错误。
-
fail()触发后,通常可继续使用流(如调用clear()后重试),但需先跳过错误输入(用ignore()或seekg()) -
bad()一旦为true,流基本不可恢复,应立即关闭并报错 -
eof()单独为true不代表错误,只说明已到末尾;但它常和fail()同时为true(比如最后一次读操作刚好读到末尾)
打开文件时怎样可靠捕获路径或权限错误
构造 std::ifstream 或 std::ofstream 后,不能只靠默认构造是否抛异常——标准库默认禁用异常,必须显式开启。否则,即使路径不存在,is_open() 返回 false,但流对象本身不会崩溃。
推荐做法是:打开后立刻检查 is_open(),而不是等第一次读写才暴露问题。尤其在 Windows 上,中文路径、相对路径拼接错误、UAC 权限限制都容易导致静默失败。
立即学习“C++免费学习笔记(深入)”;
- 启用异常:在打开前调用
stream.exceptions(std::ios_base::failbit | std::ios_base::badbit) - 不启用异常时,必须在
open()后立即检查:if (!file.is_open()) { /* 处理错误 */ } - Linux 下注意
errno值:open()失败时,strerror(errno)比流状态更具体(如No such file or directoryvsPermission denied)
读取循环中为什么 while (!file.eof()) 是危险的
这是 C++ 文件流最经典误用。因为 eof() 只在尝试读取失败后才被置位,所以 while (!file.eof()) 会导致最后一次读取失败后仍进入循环体,用的是上一次残留的无效数据。
正确模式永远是“先读、再判断”,把读操作放在循环条件里:
int x;
while (file >> x) {
// x 已成功读取
}
// 此处 file.fail() 为 true,但 x 是上一次有效值
- 对
getline():用while (std::getline(file, line)) - 对
read():检查返回值,file.gcount()给出实际字节数 - 如果需要区分 EOF 和其他错误,在循环退出后单独查
file.eof()和file.fail()
写入失败后如何知道是磁盘满还是路径不存在
写入错误(如 ofstream 失败)通常不会立刻暴露原因。系统级错误码被封装在流状态中,需结合 <code>errno 或平台 API 获取细节。
Linux/macOS 下可在写操作后检查:if (file.fail() && errno == ENOSPC) {...};Windows 下可用 GetLastError(),但前提是流底层句柄未被封装掉(标准库不保证暴露句柄)。
- 最稳妥方式:写入前预检——用
access()(POSIX)或GetFileAttributes()(Win32)验证目录可写 - 小文件写入后强制
flush()并检查状态,因为缓冲区满或磁盘满可能延迟到flush()或析构时才触发bad() - 日志中不要只记
"write failed",要附带file.tellp()(当前写位置)和errno字符串
流错误不是一次性开关,而是状态叠加。一次失败后不清除标志,后续所有操作都会立刻失败。最容易被忽略的是忘记在重试前调用 clear(),尤其在交互式程序中反复打开同一文件时。



















