C++17起应使用std::filesystem::recursive_directory_iterator递归遍历并计数,需启用-std=c++17、包含<filesystem>头文件、捕获filesystem_error异常,并用is_regular_file()筛选文件;Windows/Linux均跨平台支持,自动处理符号链接与权限异常。

用 std::filesystem::recursive_directory_iterator 遍历目录并计数
从 C++17 开始,std::filesystem 是最直接、跨平台的方案。它能天然处理符号链接、权限拒绝等边界情况,比手写递归或调用系统命令更可靠。
关键点在于:默认构造的 recursive_directory_iterator 会自动进入子目录;需捕获 std::filesystem::filesystem_error,否则遇到无权限目录(如 /proc 下某些条目)会直接抛异常崩溃。
- 使用
std::distance要小心——它底层是线性遍历,但无法跳过错误项,仍需手动 try/catch - 若只需文件数量(不含目录),检查
iter->is_regular_file();若含目录,用is_directory()或直接计数所有 entry - Windows 下路径分隔符自动兼容,但注意
\?前缀不被std::filesystem支持,超长路径仍可能失败
#include <filesystem>
#include <iostream>
int count_files_recursive(const std::string& path) {
int count = 0;
try {
for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) {
if (entry.is_regular_file()) ++count;
}
} catch (const std::filesystem::filesystem_error& e) {
// 忽略无权限目录,或记录日志
std::cerr << "Skip: " << e.what() << '
';
}
return count;
}
Windows API 方式:用 FindFirstFileExW + 手动递归
适用于必须支持 C++14 或更低标准、或需要精细控制(如跳过特定文件名、限制深度)的场景。但代码量大、易出错,且 Windows-only。
核心陷阱是:不能直接对 "C:\dir\*" 递归调用 FindFirstFileExW —— 它只匹配单层;必须先获取子目录列表,再逐个递归调用。
立即学习“C++免费学习笔记(深入)”;
- 务必用宽字符版本(
FindFirstFileExW),否则中文路径乱码 - 每次
FindFirstFileExW后必须配对FindClose,否则句柄泄漏 -
WIN32_FIND_DATAW中cFileName是文件名,不是完整路径;拼接时注意结尾反斜杠 - 递归前检查
dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY,且排除"."和".."
Linux/macOS 下用 opendir/readdir 的注意事项
POSIX 方案在非 Windows 平台稳定,但需自己处理符号链接循环、权限错误和内存管理。相比 std::filesystem,它更轻量但更底层。
常见错误是:未检查 readdir 返回 nullptr 就访问结构体字段,或忘记 closedir 导致 fd 耗尽。
-
struct dirent*的d_type字段并非所有文件系统都支持(如某些 NFS),应 fallback 到stat() - 递归前用
realpath()或哈希记录已访问 inode+device,防止软/硬链接导致无限循环 - 不要用
scandir()然后qsort——除非真需要排序,否则纯浪费 CPU 和内存
性能差异与何时该换方案
单纯统计数量时,std::filesystem::recursive_directory_iterator 在小目录下足够快;但当目录层级深、文件极多(>10 万)时,频繁构造 std::filesystem::path 对象会产生可观开销。
- 若只需要总数,避免创建每个
path对象:用entry.path().c_str()获取原始字符串,或用entry.file_size()前先判断类型 - 极端性能场景(如备份工具),可考虑
getdents64(Linux)或fts_open(POSIX),但移植性差、API 复杂 - 别为“递归”强行用递归——现代文件系统有深度限制(如 ext4 默认 64 层),实际中栈溢出风险远低于权限/符号链接问题
真正难的不是怎么写循环,而是决定要不要进某个子目录:权限拒绝、挂载点、用户指定排除模式……这些逻辑一旦加进去,std::filesystem 的简洁性就迅速瓦解,此时封装成独立函数并明确错误处理策略比追求“一行解决”更重要。


















