跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

c++怎么在读写文件流时通过状态位判断磁盘是否已满【避坑】

磁盘满时std::ofstream不立即设failbit/badbit,需在写失败后紧邻检查errno是否为ENOSPC(Linux/macOS)或_doserrno映射值(Windows),good()等状态检查无法预测磁盘满。 检查
failbit
和
badbit
不足以判断磁盘满 磁盘写满时,C++ 标准库的
std::ofstream
通常不会立即设置
failbit
或
badbit
—— 它可能先缓存数据,直到
flush()
或析构时才真正尝试落盘。此时才可能触发失败,但错误已被“延迟暴露”。更关键的是:
failbit
可能由格式错误、权限不足、路径不存在等多种原因引起;
badbit
多指向底层流缓冲区崩溃(如
write()
系统调用返回
EIO
),而磁盘满实际常返回
ENOSPC
,它往往被封装在
errno
中,标准流对象并不直接暴露它。 必须结合
errno
和系统级检查才能可靠识别
ENOSPC
当
ofstream
写入失败(例如
!os
为真或
os.fail()
返回
true
)后,应立刻检查
errno
值是否为
ENOSPC
。但注意:不同平台对
errno
的保留行为不一致,需在失败后**紧邻地**读取,且不能有其他可能修改
errno
的系统调用穿插。
std::ofstream
的
clear()
会重置状态位,但**不会清空
errno
**;而
close()
或析构可能触发二次写入,覆盖
errno
Linux/macOS 下可直接用
#include
和
ENOSPC
;Windows 需用
_doserrno
并映射(
_doserrno == ERROR_HANDLE_DISK_FULL
或
ERROR_DISK_FULL
) 示例逻辑:
std::ofstream os("data.bin", std::ios::binary); os.write(buf, size); if (!os) { int saved_errno = errno; // 必须立刻保存 if (saved_errno == ENOSPC) { // 磁盘已满 } }
避免依赖
good()
或
operator bool()
做提前预判
good()
是
!fail() && !bad() && !eof()
的组合,它只反映流对象自身的状态,完全无法预测下一次写是否成功——尤其对磁盘满这种外部资源限制,流对象无感知。试图用
os.seekp(0, std::ios::end); os.tellp()
推算剩余空间更是危险:文件系统预留空间、配额、稀疏文件、快照机制都会让结果失真。 不要写
if (os.good()) os 来“预防”失败——这毫无意义
不要用
statfs()
/
GetDiskFreeSpaceEx()
检查剩余空间再写,因为两次调用之间可能被其他进程占满 正确做法是:写 → 检查失败 → 查
errno
→ 分情况处理(
ENOSPC
就清理/告警,
EACCES
就改权限,
ENOSPC
和
EIO
的处理策略完全不同) 析构时隐式
close()
导致
errno
覆盖是高频坑点 如果未显式调用
close()
,
ofstream
析构时会自动 flush 并 close。此时若发生
ENOSPC
,
errno
就在析构内部被覆盖,你根本来不及捕获。因此: 立即学习 “ C++免费学习笔记(深入) ”; 务必显式调用
os.close()
,并在其后**立刻**检查
errno
或使用 RAII 封装,在自定义析构中捕获失败(但需确保异常安全,避免在析构中抛异常) 更稳妥的做法是:每次
write()
后都检查状态 +
errno
,而不是等到最后 Linux 下还可配合
sync_file_range()
或
fdatasync()
强制落盘并捕获早期错误,但会牺牲性能 磁盘满不是流状态机的一部分,它是操作系统内核返回的具体错误码。绕过
errno
直接靠 C++ 流接口判断,等于在盲区里开车。

相关文章