c++17 的 std::filesystem::recursive_directory_iterator 是遍历目录树的标准方案,需启用 -std=c++17、包含头文件、捕获 filesystem_error,并注意符号链接默认不跟随;替代方案如 win32 api 或 posix opendir 需手动处理路径、编码、递归及错误,且易出错。

用 std::filesystem::recursive_directory_iterator 遍历最直接
这是 C++17 引入的标准方案,不用依赖第三方库,也不用手动处理路径拼接或符号链接循环。只要编译器支持 C++17(GCC 8+、Clang 7+、MSVC 2017 15.7+),就能开箱即用。
常见错误是误用 std::filesystem::directory_iterator —— 它只遍历一级,漏掉所有子目录;还有人试图用 std::filesystem::is_regular_file() 判断时没加异常处理,遇到权限不足的目录直接崩溃。
- 必须在编译时启用 C++17:加
-std=c++17(GCC/Clang)或/std:c++17(MSVC) - 记得
#include <filesystem></filesystem>,MSVC 还需链接shlwapi.lib(VS2019 起通常自动) - 用
try-catch包住迭代过程,跳过无权限访问的子目录(std::filesystem::filesystem_error) -
recursive_directory_iterator默认会跳过符号链接指向的目录(避免循环),如需跟随,得传std::filesystem::directory_options::follow_directory_symlink
#include <filesystem>
#include <iostream>
int count_files(const std::filesystem::path& p) {
int n = 0;
try {
for (const auto& entry : std::filesystem::recursive_directory_iterator(p)) {
if (entry.is_regular_file()) ++n;
}
} catch (const std::filesystem::filesystem_error&) {
// 忽略无法访问的子目录,不中断计数
}
return n;
}</iostream></filesystem>
Windows 下用 FindFirstFileExW + 手动递归更轻量但易出错
如果你不能用 C++17,或者对启动速度极度敏感(比如嵌入式或高频调用场景),原生 Win32 API 确实更快,但代价是路径处理、编码、递归栈管理全得自己控。
典型翻车点:宽字符路径没用 std::wstring 存,FindFirstFileExW 返回的 WIN32_FIND_DATAW 中文件名是 UTF-16,混用 char* 直接乱码;还有人忘了在递归前检查 dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY,把 . 和 .. 当成真实子目录反复钻进去。
- 必须用
FindFirstFileExW(不是FindFirstFileA),路径参数传std::wstring - 每次进入子目录前,过滤掉
L"."和L"..",否则无限递归 - 递归调用前要确保子路径长度不超过 MAX_PATH(除非启用长路径支持,且系统已配置
LongPathsEnabled=1) - 别忘了
FindClose,否则句柄泄漏
Linux/macOS 下 opendir/readdir 需手动处理符号链接和深度限制
POSIX 方案兼容性最好,连老版本 GCC 都能跑,但标准库不提供递归封装,你得自己写栈或队列模拟遍历,还要决定是否允许软链循环。
最容易被忽略的是:默认 readdir 不区分文件类型,stat() 每次调用都有 syscall 开销;而如果跳过 stat 直接靠名字判断(比如后缀为 .txt),就会漏掉无扩展名的脚本、二进制文件等。
- 用
struct dirent::d_type字段可免一次stat(glibc 支持,但某些文件系统可能返回DT_UNKNOWN) - 递归深度建议设上限(比如 64 层),防止恶意构造的符号链导致栈溢出
-
opendir失败时检查errno == EACCES,跳过而非中止 - 注意
readdir不是线程安全的,多线程调用需加锁或改用readdir_r(已废弃,但部分旧环境仍需)
性能差异主要来自路径解析和系统调用频次
std::filesystem::recursive_directory_iterator 在底层仍会触发大量 stat 或 GetFileAttributesEx,但它做了缓存和批处理优化;手写 POSIX/Win32 实现如果每项都调一次 stat,实际耗时可能翻倍。
真正影响大文件夹统计速度的,往往不是算法,而是磁盘 I/O 模式:顺序读取目录项快,随机跳转查属性慢。所以别在循环里反复拼接完整路径再 stat,尽量复用 dirent 或 WIN32_FIND_DATA 中已有字段。
- Linux 上用
getdents64(非标准但高效)可绕过 glibc 封装,但丧失可移植性 - Windows 上开启
FILE_FLAG_BACKUP_SEMANTICS可提升对大量小文件的遍历速度(需管理员权限) - 如果只是粗略估算,可跳过
is_regular_file(),只统计非目录项(但会把 FIFO、socket、设备文件也计入)
递归深度大、权限碎片化、符号链接环——这三类情况,任何方案都要主动防御,不能指望“理论上正确”就不出问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











