std::filesystem::recursive_directory_iterator是C++17起最省事的标准目录递归遍历方案,自动处理符号链接、权限异常和路径编码,但需预检路径存在性并捕获filesystem_error异常。

用 std::filesystem::recursive_directory_iterator 最省事
C++17 起,std::filesystem 是标准方案,不用依赖 Boost 或系统 API。它能自动处理符号链接、权限异常、路径编码等边界情况,比手写递归或调用 FindFirstFile / opendir 更可靠。
常见错误是忽略异常捕获——比如遇到无权限目录或损坏的符号链接时,recursive_directory_iterator 默认抛 std::filesystem::filesystem_error,不 catch 就直接 crash。
- 遍历前加
if (!std::filesystem::exists(path) || !std::filesystem::is_directory(path))预检 - 用
try { ... } catch (const std::filesystem::filesystem_error& e)包住迭代循环 - 如果只要文件(不含子目录),对每个
entry判断entry.is_regular_file() - 注意:Windows 下路径分隔符用
/或\都行,std::filesystem内部会归一化
for (const auto& entry : std::filesystem::recursive_directory_iterator("data")) {
if (entry.is_regular_file()) {
std::cout << entry.path().filename().string() << "
";
}
}
std::filesystem::directory_iterator 适合非递归场景
如果只需要当前目录下的文件名(不进子目录),用 directory_iterator 更轻量,性能略好,也不会触发深层异常。
容易踩的坑是误以为它会跳过 . 和 .. ——其实不会,C++ 标准要求它返回所有目录项,包括这两个特殊条目,必须手动过滤。
立即学习“C++免费学习笔记(深入)”;
- 每次取
entry.path().filename().string()得到纯文件名(不含路径) - 用
entry.path().filename() == "." || entry.path().filename() == ".."跳过 - Linux/macOS 下注意文件名编码:如果目录含中文,确保终端和 locale 支持 UTF-8,否则
string()可能乱码
Windows 下别硬上 FindFirstFile,除非真要控制细节
老代码里常见用 FindFirstFile + FindNextFile 手动遍历,但它只支持 Windows,路径通配固定为 *.*,且需自己处理 Unicode(W 版本返回 wchar_t*,转 std::string 易出错)。
真正需要它的场景极少:比如必须跳过重解析点(reparse points)、或在遍历中动态修改文件属性并立即生效。
- 用
FindFirstFileW,传入L"data\*"(注意结尾*,不是*.*) -
WIN32_FIND_DATAW中的cFileName是宽字符,用std::wstring_convert<std::codecvt_utf8<wchar_t>>转换风险高,建议直接存std::wstring - 忘记调用
FindClose会导致句柄泄漏,尤其在循环中反复打开时
性能与路径拼接的隐性开销
高频调用 entry.path().string() 或反复拼接路径(如 dir + "/" + name)会触发多次内存分配,尤其在海量小文件场景下明显拖慢速度。
更稳的做法是:只在真正需要完整路径时才构造,其余时候用 entry.path().filename()(返回 std::filesystem::path,轻量);拼接统一用 / 运算符:base / entry.path().filename(),它内部优化过,不涉及字符串拷贝。
- 避免把
entry.path()存成std::string后再切分——path对象本身支持parent_path()、stem()、extension()等零开销访问 - 如果只是收集文件名(无路径),直接 push
entry.path().filename().string(),别先存全路径再substr() - Release 模式下
std::filesystem性能足够,Debug 下可能慢几倍,这是正常现象,不是 bug
路径对象的生命周期和迭代器绑定紧密,别把 entry.path() 存成裸指针或引用——迭代器前进后,旧 entry 就失效了。










