C++17起推荐用std::filesystem::recursive_directory_iterator遍历目录树;需捕获filesystem_error异常,用is_regular_file()过滤非文件项,优先调用filename().string()而非path().string()以提升性能。

用 std::filesystem::recursive_directory_iterator 最直接
从 C++17 开始,std::filesystem 是标准方案,不用依赖 Boost 或平台 API。它能天然处理嵌套子目录,且路径对象自带类型安全和跨平台语义。
常见错误是忽略异常——比如权限不足或路径被删除时,recursive_directory_iterator 默认抛 std::filesystem::filesystem_error,不捕获会导致程序崩溃。
实操建议:
- 务必在循环外或循环内加
try/catch捕获std::filesystem::filesystem_error - 用
it->is_regular_file()过滤掉目录、符号链接等非文件项(默认迭代器会遍历所有条目) - 避免在循环中反复调用
it->path().string()—— 如果只需文件名,用it->path().filename().string()更轻量 - Windows 下注意路径分隔符:
std::filesystem::path内部统一用/或\无关,但输出给用户时可用.generic_string()避免混合斜杠
FindFirstFileW
- 每次调
FindFirstFileW前,先检查目录是否存在且可读(GetFileAttributesW返回INVALID_FILE_ATTRIBUTES表示失败) - 遍历时跳过
L"."和L".."目录项,否则会无限递归 -
WIN32_FIND_DATAW中的cFileName是文件名,不是全路径;拼全路径需用PathCchCombine或手写安全拼接
Linux/macOS 用 opendir/readdir 不支持递归,得自己实现
opendir 只返回当前目录下条目,没有内置递归能力。你得用栈或队列存待处理目录路径,逐层 readdir,遇到子目录就 push 进去——本质是手动模拟 BFS。
容易出问题的是符号链接循环:如果目录 A 里有软链指向 B,B 里又有软链指回 A,不记录已访问路径就会死循环。POSIX 没强制要求 readdir 跳过 ./..,必须手动过滤。
实操建议:
- 用
std::set<:string>存已访问的inode + device(用stat()获取),比只记路径更可靠 - 对每个
dirent调lstat()判断类型:S_ISREG()才是普通文件,S_ISDIR()且非./..才入队 - 不要假设
readdir返回顺序——它不保证字母序,也不保证文件在前目录在后
性能差异和兼容性取舍
C++17 std::filesystem 在 Windows 上底层调 FindFirstFileExW,Linux 上用 opendir/readdir,所以性能和原生 API 接近。但它在旧编译器(如 GCC 7 以下)默认不启用,需加编译选项:-std=c++17 -lstdc++fs(GCC)或 /std:c++17(MSVC)。
Boost.Filesystem 曾是过渡方案,但现在已标记为 deprecated,不建议新项目引入。
真正容易被忽略的是:某些嵌入式或容器环境可能禁用 std::filesystem(比如 musl libc 系统),此时连编译都过不去——得提前检查 __has_include( 并 fallback 到条件编译分支。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











