c++17 std::filesystem::recursive_directory_iterator 是跨平台目录遍历的标准解法,需用 try/catch 处理异常,优先使用 it->is_regular_file() 等成员函数复用元数据,避免重复 stat 调用,并用 u8string() 统一 utf-8 编码处理路径匹配。

用 std::filesystem::recursive_directory_iterator 遍历最直接
Windows 上用 FindFirstFile、Linux 用 opendir + 手写递归,既容易漏目录又难跨平台。C++17 起 std::filesystem 是标准解法,recursive_directory_iterator 自动处理符号链接(默认不跟随)、权限错误跳过、路径拼接,不用手动拼 "dir/subdir/file"。
常见错误是没捕获异常:遇到无权限目录或断开的挂载点时,迭代器会抛 std::filesystem::filesystem_error,不处理就 crash。实际使用必须包在 try/catch 里。
- 默认不跟随符号链接,要跟随得传
std::filesystem::directory_options::follow_directory_symlink - 遍历时用
it->path()拿完整路径,别用it->path().filename()做过滤——它丢掉目录信息,没法判断是否在目标子树内 - Windows 下路径分隔符用
/或\都行,std::filesystem内部自动标准化
文件名过滤优先用 std::string_view + ends_with
查 "*.log" 或 "config.json" 这类固定后缀,别用正则——启动慢、编译依赖重、还容易写错转义。C++20 的 std::string_view::ends_with 零分配、常量时间,够用且安全。
注意 it->path().extension() 返回的是 ".log"(带点),而用户输入的通配符可能是 "log" 或 "*.log"。直接比对容易错位。
立即学习“C++免费学习笔记(深入)”;
- 统一把用户输入转成小写(如果忽略大小写),再用
path.filename().string()转成std::string_view比对 - 避免调用
path.string()全路径比对——大目录下字符串构造开销明显,filename()就够了 - Windows 文件系统不区分大小写,但
std::filesystem默认按字节比,所以需显式转小写
性能瓶颈往往卡在 is_regular_file() 和 file_size()
很多人一进来就对每个 entry 调 is_regular_file() + file_size() > 1024,结果遍历 10 万个文件慢到怀疑人生。问题在于每次调用都触发一次系统 stat 调用,磁盘 I/O 成倍放大。
其实 recursive_directory_iterator 在构造或递增时已经读取了部分元数据。用 it->is_regular_file()(注意是成员函数,不是自由函数)能复用已有信息,快一个数量级;而 file_size() 必须额外 syscall,真要筛大小建议先用 is_regular_file() 快速过滤,再对剩余少量文件查 size。
-
it->status().type()可以一次拿到类型(regular、directory、symlink 等),比多次调is_*更省 - 不要在循环里反复调
it->path().parent_path()—— 它每次新建path对象,改用局部变量缓存 - SSD 上差异不明显,但机械盘或网络文件系统(如 SMB/NFS)上,少一次 stat 就少一次寻道
跨平台路径匹配要小心 path.native() 和编码
Windows 用 UTF-16,Linux/macOS 用 UTF-8,std::filesystem::path 内部做了抽象,但一旦调 path.native() 拿原始字符串,就暴露平台差异。比如日文路径在 Windows 上转 std::string 会乱码,Linux 上却正常。
真正需要字符串比较时(比如和用户输入的 pattern 匹配),一律用 path.filename().u8string() 转 UTF-8 std::string,确保一致。别信 “我本地测试没问题” —— 中文路径在 CI 的 Linux 容器里大概率崩。
- 用户输入的搜索词,也统一用 UTF-8 解释(比如从命令行参数或 config 文件读入),别用
std::locale做本地化转换——不可靠且慢 - VS2019+ 默认开启
/utf-8编译选项,但 MinGW 和旧版 Clang 可能需要手动指定 - 如果最终要输出路径给用户看,用
path.string()(Windows 下是宽字符,Linux 下是 UTF-8),别硬塞进printf或 C 风格日志
递归深度、符号链接环、权限边界这些不是“加个 flag 就解决”的事,得结合具体场景做裁剪——比如只查两级子目录,就别用 recursive,改用两层 for 循环更稳。










