应先用 is_regular_file() 过滤再调用 file_size() 并捕获异常,构造 recursive_directory_iterator 时启用 skip_permission_denied 选项,避免因权限、符号链接或特殊文件(如 /proc)导致崩溃或漏统计。

用 std::filesystem::file_size 递归累加会出错?
直接对每个 std::filesystem::directory_entry 调用 file_size() 并累加,大概率抛出 std::filesystem::filesystem_error —— 常见报错是 Operation not permitted 或 Permission denied,尤其遇到符号链接、挂载点、/proc /sys 等特殊目录时。
根本原因是:file_size() 对非常规文件(如目录、socket、设备节点)行为未定义;对不可读路径直接失败,不跳过。
- 必须先用
is_regular_file()过滤,只统计普通文件 - 对目录本身调用
file_size()是无效操作(返回值无意义),应跳过 - 遇到权限不足或访问异常,需捕获
std::filesystem::filesystem_error,而非让程序崩溃
Windows 下 GetDiskFreeSpaceEx 不能替代递归统计
GetDiskFreeSpaceEx 返回的是卷级空闲/总空间,和“某个目录下所有文件物理占用”完全不是一回事。NTFS 压缩、稀疏文件、硬链接、重解析点都会导致单个文件的 file_size() ≠ 实际磁盘占用;而 GetDiskFreeSpaceEx 完全不感知目录结构。
真正需要的是:遍历路径下所有可访问的普通文件,累加其 file_size() —— 这才是用户说的“物理占用大小”的合理近似(注意:不是精确扇区占用,但符合绝大多数场景预期)。
立即学习“C++免费学习笔记(深入)”;
-
file_size()返回的是逻辑大小(字节数),对压缩文件也返回解压后大小,这是有意设计,不是 bug - 若真要查实际磁盘占用(如 NTFS 压缩后的簇占用),得用
GetCompressedFileSize(Windows)或stat.st_blocks * 512(Linux),但跨平台成本高,且用户通常不需要这个粒度 - 递归时务必用
std::filesystem::recursive_directory_iterator,别手写栈模拟,它自动处理循环符号链接(默认禁用)和权限跳过逻辑
递归遍历时如何安全跳过异常路径
关键不是“避开所有错误”,而是让迭代器在出错时不中断整个遍历。std::filesystem::recursive_directory_iterator 构造时传入 std::filesystem::directory_options::skip_permission_denied,就能跳过权限不足的子目录,继续后续路径。
但仍需手动捕获单个 entry 的 file_size() 异常,因为 iterator 本身不负责检查每个 entry 的可读性。
- 构造 iterator 时加上选项:
std::filesystem::recursive_directory_iterator(path, std::filesystem::directory_options::skip_permission_denied) - 对每个
entry,先if (entry.is_regular_file()),再 try/catchfile_size() - 不要在循环里用
entry.status().type()判断类型——它可能因权限失败而抛异常;is_regular_file()内部已做容错 - 大目录下频繁异常捕获有开销,但比崩溃或漏统计强;可考虑加日志记录失败路径(如用
entry.path().string())
C++17 std::filesystem 在 macOS/Linux 的兼容细节
macOS 默认启用 APFS 快照和克隆文件,Linux 可能有 btrfs 的 CoW 特性,此时多个路径指向同一物理数据块,但 file_size() 仍按每个路径独立累加 —— 这不是 bug,是预期行为。用户若想去重统计,需额外维护 device + inode 哈希集,但极少见需求。
另外:Linux 下 /proc 和 /sys 中很多“文件”是虚拟接口,file_size() 可能返回 0 或抛异常,skip_permission_denied 不起作用,必须靠 is_regular_file() 过滤掉。
- 务必链接
-lstdc++fs(GCC)或-lc++fs(Clang),否则链接失败 - 某些旧版 libstdc++(如 GCC 8.2 前)对
recursive_directory_iterator的异常处理有缺陷,建议 GCC ≥ 9.1 / Clang ≥ 8 - 路径末尾带斜杠(如
"./dir/")不影响行为,但std::filesystem::path会标准化,无需手动 trim
递归统计看似简单,最麻烦的永远是边界:符号链接是否跟随、挂载点是否进入、设备文件怎么跳过、异常发生时要不要记日志——这些不写进主逻辑,就容易在线上环境突然卡住或少算几百 GB。



















