C++17起推荐用std::filesystem::recursive_directory_iterator:自动处理符号链接、跨平台路径、迭代语义清晰;需捕获filesystem_error或设skip_permission_denied选项;避免循环中频繁构造path和调用status()。

用 std::filesystem::recursive_directory_iterator 最省心
C++17 起,std::filesystem 是标准方案,不用自己拼路径、判类型、管栈深度。它自动跳过符号链接(默认)、处理跨平台路径分隔符,且迭代器语义清晰。
常见错误是忽略异常——比如遇到权限不足的子目录,recursive_directory_iterator 默认抛 std::filesystem::filesystem_error,不 catch 就崩。
- 加
try-catch包裹迭代循环,或构造时传std::filesystem::directory_options::skip_permission_denied - 想跳过某些子目录(如
.git),得手动检查iter->path().filename(),不能靠过滤器自动跳过 - Windows 下长路径(>260 字符)可能失败,需确保编译时启用 long path support(manifest 或注册表配置)
Windows 上用 FindFirstFileExW 需要手动递归
没 C++17 或必须兼容旧标准时,Win32 API 是底线选择。但 FindFirstFileExW 本身不递归,你得自己维护路径栈或用递归函数——容易栈溢出或漏清资源。
典型翻车点:宽字符处理不一致。传入路径必须是 LPCWSTR,用 std::wstring 拼接时若混入 char* 或没设 locale,FindNextFileW 会返回空或乱码文件名。
立即学习“C++免费学习笔记(深入)”;
- 所有路径操作统一用
std::wstring,避免MultiByteToWideChar手动转换 - 每次进入子目录前,保存当前
hFind并关闭,否则句柄泄漏 -
WIN32_FIND_DATAW中的cFileName是数组,不是指针,别直接当std::wstring构造参数用(要用std::wstring(data.cFileName))
Linux/macOS 下 opendir/readdir 的隐性陷阱
POSIX 方案看着简单,但 readdir 不保证顺序、不跳过 . 和 ..,更不会自动进子目录——全得手写逻辑。最常被忽略的是 errno 值复位问题。
比如某次 readdir 返回 nullptr,你以为到头了,其实可能是 errno == EACCES(权限不够),但下一轮循环前没清 errno,就误判为结束。
- 每次调用
readdir前先置errno = 0 - 用
stat判文件类型时,注意st_mode & S_IFMT再比对S_IFDIR,别直接比st_mode == S_IFDIR - 路径拼接用
snprintf或std::string::append,别用strcat——源目缓冲区重叠就 UB
性能敏感时别在循环里反复调用 std::filesystem::status
遍历时如果每个文件都调一次 status() 判类型,开销很大——它实际发了一次系统调用。而 recursive_directory_iterator 的 iter->is_directory() 是缓存结果,快得多。
另一个坑是频繁构造 std::filesystem::path 对象。比如在循环里写 path / "sub" / "file.txt",每轮都新建临时对象;改成预先拼好 base path,再用 operator/= 复用更稳。
- 优先用
iter->is_regular_file()、iter->is_directory()等成员函数,而非单独status() - 避免在热循环中做字符串分割(如按
/拆路径),改用path.filename()或path.extension() - 大量小文件场景下,考虑用
std::vector批量收集路径再处理,减少 IO 密集型操作的穿插
递归遍历真正的复杂点不在“怎么走”,而在“怎么安全地停”——权限拒绝、路径循环引用、符号链接爆炸、中断信号响应,这些边界几乎不会出现在示例代码里,但上线后第一个报错往往就是它们。










