std::filesystem::recursive_directory_iterator 是 c++17 起最省心的目录递归遍历方案,自动跳过符号链接、支持 unicode、异常安全;需启用 c++17 标准并注意手动过滤和异常捕获。

用 std::filesystem::recursive_directory_iterator 最省心
C++17 起,std::filesystem 是标准方案,不用自己写递归、不依赖 Boost 或系统 API。它自动跳过符号链接(默认行为),能正确处理 Unicode 路径(在 Windows 上尤其关键),且异常语义清晰。
常见错误是直接用 directory_iterator —— 它只扫一层,漏掉子目录;或者没捕获 filesystem_error,遇到权限不足或断开的网络路径就 crash。
- 必须开启 C++17 或更高标准:编译时加
-std=c++17(GCC/Clang)或/std:c++17(MSVC) - Windows 下需链接
shlwapi.lib(MSVC 默认连,但自定义构建链可能漏) - 遍历时若想跳过某些目录(如
.git),得手动检查iter->path().filename(),recursive_directory_iterator本身不提供过滤回调
for (const auto& iter : std::filesystem::recursive_directory_iterator("/path")) {
if (iter.is_directory()) {
std::cout << "[DIR] " << iter.path() << "\n";
} else if (iter.is_regular_file()) {
std::cout << "[FILE] " << iter.path() << "\n";
}
}
Windows 上用 FindFirstFileW + 手动递归要防栈溢出
老项目或需兼容 C++14 时,Win32 API 是绕不开的。但 FindFirstFileW 只返回单层结果,必须自己写递归 —— 这里最容易踩坑的是深度过大的目录树导致栈溢出,或没正确跳过 "." 和 ".." 导致无限循环。
- 务必用
FindFirstFileW(宽字符),别用A版本,否则中文路径全乱码 - 每次进入子目录前,检查
cFileName是否为L"."或L"..",跳过它们 - 递归调用前建议加深度限制(比如 max_depth = 32),避免恶意构造的嵌套目录炸掉栈
- 记得配对调用
FindClose,否则句柄泄漏
Linux/macOS 下 opendir/readdir 需手动处理符号链接
opendir + readdir 是 POSIX 标准做法,轻量、无依赖,但不会自动解符号链接 —— 如果目标目录里有软链接指向父目录(比如 ./loop -> ..),不加判断就会无限递归。
立即学习“C++免费学习笔记(深入)”;
- 用
lstat()替代stat()获取原始 inode 信息,避免误入链接目标 - 遇到
S_IFLNK类型时,按需决定是否chdir进去(通常应跳过) -
readdir返回的d_name不带路径,拼接时容易少写/,推荐用snprintf或std::format(C++20)安全拼接 - 注意
errno == ENOENT或EACCES时继续循环,别直接 return
跨平台封装要注意 std::filesystem::status() 的异常时机
写一个跨平台扫描函数时,很多人在循环里对每个路径调用 status() 判断类型,但这个函数在路径不可达时会抛 filesystem_error —— 而不是返回 file_status 中的 status_known() == false。这和直觉相反,也容易让日志丢失上下文。
- 更稳的做法是:先用
exists(p)做快速探路(它内部 catch 异常并返回 false),再调用status(p) - 或者直接用
is_directory(p)/is_regular_file(p),它们也自带异常防护,语义更明确 - MacOS 上 HFS+ 卷可能返回
status_unknown,此时别假设是目录或文件,跳过即可
真正麻烦的从来不是“怎么开始递归”,而是“路径不可读时要不要停”“符号链接要不要跟”“名字含控制字符怎么办”。这些边界情况没被 recursive_directory_iterator 隐藏掉,只是换了个地方暴露——比如你得自己 decide 是否忽略 filesystem_error 中的 permission_denied。










