C++17中用std::filesystem::recursive_directory_iterator遍历累加文件大小需捕获filesystem_error异常,过滤普通文件并用uintmax_t累加,避免溢出和崩溃。

用 std::filesystem::recursive_directory_iterator 遍历并累加文件大小
这是 C++17 起最直接、跨平台的方式。它自动处理符号链接(默认跳过)、权限拒绝路径(抛异常)和递归进入子目录,无需手动维护栈或队列。
关键点在于:必须捕获 std::filesystem::filesystem_error,否则遇到无权限目录(如 /proc 下某些项)或损坏的符号链接时程序会崩溃。
- 使用
std::filesystem::is_regular_file()过滤掉目录、socket、设备文件等非普通文件 - 调用
std::filesystem::file_size()获取单个文件大小,它返回uintmax_t,能覆盖大文件(如 >4GB) - 对每个合法文件做累加,用
uintmax_t或uint64_t存总和,避免int溢出
uintmax_t total = 0;
try {
for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) {
if (std::filesystem::is_regular_file(entry.status())) {
total += std::filesystem::file_size(entry);
}
}
} catch (const std::filesystem::filesystem_error& e) {
// 例如:Permission denied 或 No such file or directory
std::cerr << "Warning: " << e.what() << "
";
}
为什么不用 std::filesystem::directory_iterator?
std::filesystem::directory_iterator 只遍历单层,不进子目录。若手动实现递归,需自己处理异常传播、循环引用(如硬链接/挂载点)、栈深度限制等问题,容易漏掉错误或陷入无限遍历。
而 recursive_directory_iterator 内部已处理这些——它跳过循环符号链接(默认行为),且支持 disable_symlinks() 和 follow_symlinks() 显式控制;还提供 pop() 接口在异常后继续遍历同级其他项。
立即学习“C++免费学习笔记(深入)”;
- 若你明确要跳过所有符号链接,构造时传
std::filesystem::directory_options::skip_permission_denied - 若想统计符号链接本身大小(而非目标),需额外判断
entry.is_symlink()并用std::filesystem::symlink_status()
Windows 上要注意 \? 前缀与长路径
Windows 默认路径长度限制为 260 字符,超过会触发 filesystem_error,错误码为 ERROR_FILENAME_EXCED_RANGE。此时需启用长路径支持,并在路径前加 \\?\ 前缀(仅 Windows 有效)。
- 确保系统启用组策略“启用 Win32 长路径”(
ComputerWindows SettingsLocal PoliciesSecurity Options) - 代码中对 Windows 路径预处理:
std::string wide_path = "\\?\" + path.string();,再转为std::filesystem::path - 注意:
recursive_directory_iterator对\\?\路径完全兼容,但部分旧版 MSVC 工具链可能要求链接Shlwapi.lib
性能瓶颈往往不在迭代器,而在 file_size() 系统调用
每次 file_size() 都是一次 stat()(Linux/macOS)或 GetFileAttributesEx()(Windows)系统调用。对海量小文件(如日志目录下数万 .log),这会成为 I/O 瓶颈。
- 可考虑先用
entry.file_size()(C++20 引入),它复用迭代器已获取的元数据,避免重复系统调用 - 若用 C++17,只能接受开销;或改用平台 API 批量获取(如 Linux 的
getdents64+statx,但失去可移植性) - 磁盘缓存会影响实测速度——首次运行慢,后续快;SSD 上差异更小,HDD 上尤其明显
实际项目里,如果只是偶尔统计、且目录层级不深,recursive_directory_iterator 加 file_size() 就够用。真有性能敏感场景,得先确认是 CPU、I/O 还是系统调用次数卡住,再决定是否换方案。


















