C++recursive_directory_iterator递归
recursive_directory_iterator:C++里那个“不敲门就进所有房间”的文件遍历器
你有没有试过写个程序,想把某个文件夹下所有 .cpp 文件找出来?结果发现 std::filesystem::directory_iterator 只能扫一层——进到 src/ 就停了,src/utils/ 和 src/net/ 里的文件它理都不理。这时候,你大概率会翻文档,看到一个名字很长、带 recursive 的家伙:std::filesystem::recursive_directory_iterator。
它不是“加强版的 directory_iterator”,而是一套自带栈的深度优先遍历引擎——它真会一层层往下钻,直到每个角落都踩过,而且还能随时“上楼”回退。
它到底怎么工作的?
别被名字吓住。recursive_directory_iterator 本质是个迭代器,但它内部维护了一个调用栈(stack of directories),每次 ++it 时,它先看当前目录里还有没有没处理的子项;如果没有,就自动 pop 出栈,回到父目录继续找下一个兄弟目录。这个行为,和手写 DFS 遍历时用 std::stack<std::filesystem::path> 管理待访问路径,逻辑完全一致——只是标准库帮你封装好了。
关键点在于:它默认跳过符号链接(symlinks),且遇到权限不足的目录时,不会崩溃,而是跳过(可通过 std::filesystem::directory_options::skip_permission_denied 显式控制)。这点很务实:你不想因为 /proc/1/fd 权限问题,让整个扫描中途跪倒。
一个真正能跑的最小示例
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main(int argc, char* argv[]) {
if (argc != 2) return 1;
fs::path root{argv[1]};
if (!fs::exists(root) || !fs::is_directory(root)) {
std::cerr << "Not a valid directory.\n";
return 1;
}
for (const auto& entry : fs::recursive_directory_iterator(root)) {
if (entry.is_regular_file() && entry.path().extension() == ".cpp") {
std::cout << entry.path() << '\n';
}
}
}
注意:必须传入合法目录路径,否则构造时可能抛 std::filesystem::filesystem_error。别指望它帮你兜底判断存在性。
深度控制:别让它钻到“地心”
默认它会一路递归到底。但现实里,你常需要限制深度——比如只查两层:project/src/ 和 project/src/*,但不想进 project/src/third_party/boost/ 这种嵌套几十层的坟场。
标准库没直接提供 max_depth 参数,但给了 depth() 成员函数:
for (auto it = fs::recursive_directory_iterator(root);
it != fs::recursive_directory_iterator{};
++it) {
if (it.depth() >= 2) {
it.pop(); // 主动弹出当前目录,跳过其子项
continue;
}
if (it->is_regular_file() && it->path().extension() == ".h") {
std::cout << it->path() << '\n';
}
}
it.pop() 是关键动作——它让迭代器立刻“上一层”,相当于 DFS 中的 return。这不是跳过当前项,而是放弃整个子树。用对了,比手动计数+break 清晰得多。
性能不是玄学:什么时候该换思路?
recursive_directory_iterator 方便,但有隐含成本:
- 每次
++it都要打开/读取目录句柄(尤其在 Windows 上开销明显); - 深层嵌套时,内部栈可能累积数百个
path对象; - 如果你只需要统计文件数,却对每个
entry都调用is_regular_file(),其实entry.file_type()更快(避免额外系统调用)。
所以,当你的场景是:
✅ 扫描深度可控(≤5 层)、目录总数 < 10 万;
✅ 需要灵活跳过某些子目录(如 .git, build);
✅ 希望代码简洁、可读性强;
——那它就是首选。
但如果你要处理百万级小文件,或需严格控制 IO 调度(比如嵌入式设备),就得考虑 readdir + 手写栈,或者分批异步扫描。
实用技巧三则
- 跳过特定目录:在循环中检查
entry.path().filename(),遇到.git或node_modules直接it.disable_recursion_pending()—— 这个调用会让它不进入该目录,但继续遍历同级其他项。 - 跨平台路径拼接:永远用
/操作符,比如entry.path() / "config.json",别用字符串拼接。std::filesystem::path会自动适配分隔符。 - 异常处理别偷懒:在循环内捕获
std::filesystem::filesystem_error,打印what()并continue,而不是让整个程序退出。真实环境里,权限问题太常见了。
recursive_directory_iterator 不是什么黑科技,它只是把“人肉递归”的重复劳动标准化了。它不会替你决定哪些文件重要,也不会自动并行加速——但它稳稳站在 C++20 的标准肩上,不依赖第三方,不玩虚的。下次你再为目录遍历卡壳,不妨先把它请出来,看看它能不能直接把活干完。实在不行,再动手造轮子也不迟。


还没有评论,来说两句吧...