c++17起应使用std::filesystem::recursive_directory_iterator递归遍历目录树,需启用c++17标准、处理权限异常、复用status()避免重复系统调用、正确比较扩展名并捕获filesystem_error继续搜索。

用 std::filesystem::recursive_directory_iterator 遍历目录树
从 C++17 开始,std::filesystem 是标准方案,无需第三方库或平台 API。它天然支持递归遍历,且能自动处理符号链接(默认不跟随,避免循环),比手写递归或 Win32/POSIX API 更安全简洁。
关键点:构造迭代器时传入 std::filesystem::directory_options::skip_permission_denied,否则遇到无读权限目录会抛 std::filesystem::filesystem_error 异常,直接中断整个搜索。
- 必须在编译时启用 C++17 或更高标准(如
g++ -std=c++17) - Linux/macOS 下需链接
-lstdc++fs(GCC 早期版本),Clang 12+ 和 MSVC 通常无需额外链接 - 迭代器默认跳过
.和..,无需手动过滤
用 path.extension() 精确匹配扩展名
path.extension() 返回的是 std::filesystem::path 类型,不是字符串;直接与 ".cpp" 比较会失败——因为前者可能含前导点,后者是字面量,但更隐蔽的问题是大小写敏感性。
常见错误:写成 p.path().extension() == ".cpp",实际应调用 .string() 或 .u8string() 转换后再比较,并统一转小写(Windows 文件系统不区分大小写,但 std::filesystem 默认区分)。
- 推荐写法:
p.path().extension().u8string() == u8".log"(UTF-8 安全) - 若需忽略大小写(如匹配
.TXT、.txt),用std::ranges::equal+std::tolower投影比较,或先转小写再比 - 注意:
path.extension()对archive.tar.gz返回.gz,不是.tar.gz;如需多级扩展名,得用path.stem().extension()组合判断
捕获异常并继续搜索,别让单个错误 kill 整棵树
真实文件系统中,权限不足、路径被删除、设备断开等情况极常见。如果不在循环内 try/catch,一次 filesystem_error 就会让整个 recursive_directory_iterator 提前终止。
正确做法是在 for 循环体内包裹单次迭代的 try/catch,且只捕获 std::filesystem::filesystem_error —— 其他异常(如内存分配失败)不该被静默吞掉。
- 示例结构:
for (auto& p : std::filesystem::recursive_directory_iterator(root, opts)) { try { if (p.is_regular_file() && p.path().extension().u8string() == u8".json") { results.push_back(p.path()); } } catch (const std::filesystem::filesystem_error&) { // 忽略该条目,继续下一个 continue; } } -
opts应设为std::filesystem::directory_options::skip_permission_denied,减少异常触发频率 - 不要在 catch 块里记录日志后
throw,那等于没处理
性能提示:避免重复调用 is_regular_file() 和 status()
每次调用 p.is_regular_file() 都可能触发一次 stat() 系统调用;在循环中反复判断类型+扩展名,效率低下。解决方案是复用 p.status() 的结果。
directory_entry 的 status() 方法返回缓存的 file_status,后续 is_regular_file()、is_directory() 等都基于它,无额外 syscall。
- 高效写法:
const auto st = p.status(); if (st.type() == std::filesystem::file_type::regular && p.path().extension().u8string() == u8".tmp") { ... } - 尤其在 SSD 或网络文件系统上,减少 stat 调用对整体耗时影响明显
- 注意:
p.status()可能抛异常,所以仍需放在 try 块内
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











