c++怎么使用std::filesystem遍历文件夹_c++ 递归查找文件与权限修改【技巧】

14次阅读

应使用 try/catch 包裹迭代器初始化和递增操作,配合 directory_options::skip_permission_denied 和手动 while 循环遍历,禁用符号链接跟踪以避免崩溃;查找文件时优先用 is_regular_file() 和 extension() 判断,权限修改前需捕获 Filesystem_error。

c++怎么使用std::filesystem遍历文件夹_c++ 递归查找文件与权限修改【技巧】

std::filesystem::recursive_directory_iterator 怎么用才不崩溃

直接用 std::filesystem::recursive_directory_iterator 遍历目录时,最常见崩溃原因是底层路径不可访问(比如权限不足、符号链接循环、挂载点失效),而默认构造器不捕获异常。必须显式处理 std::filesystem::filesystem_error

  • 永远用 try/catch 包裹迭代器初始化和递增操作,尤其在非受控环境(如用户目录、外部存储)中
  • 避免直接写
    for (auto& entry : std::filesystem::recursive_directory_iterator(path))

    —— 这会在首次 ++ 时触发访问,异常无法被 for 循环捕获

  • 改用 while 循环 + 手动递增,并在每次 ++iter 前检查 iter != std::filesystem::end(iter)
  • 传入 std::filesystem::directory_options::skip_permission_denied 可跳过无权读取的子目录,但注意:这仅影响「进入子目录」,不抑制对当前项元数据的读取失败

怎么安全地递归查找特定文件名或扩展名

不能依赖 entry.path().filename() 粗暴字符串匹配,因为大小写敏感性、编码、隐藏文件属性都可能出错;更可靠的是组合 entry.is_regular_file()entry.path().extension() 判断。

  • 扩展名比较务必用 == 而非 String::find:例如 entry.path().extension() == ".log",因为 .extension() 返回的是带点的 std::filesystem::path,不是裸字符串
  • 文件名精确匹配用 entry.path().filename() == "config.ini",注意它区分大小写(windows 下通常不敏感,但标准行为是敏感)
  • 若需忽略大小写,先转小写再比:用 std::ranges::transform + std::tolower 处理 entry.path().filename().string(),但注意 locale 安全性,生产环境建议用 ICU 或平台 API
  • 不要在循环中反复调用 entry.status() —— 它可能触发额外系统调用;优先用 entry.is_regular_file() / entry.is_directory(),它们复用已缓存的状态

修改文件权限时 chmod 的跨平台陷阱

std::filesystem::permissions()windows 上只模拟 unix 权限位(通过 ACL 子集),实际效果受限;linux/macOS 下才真正映射到 chmod。直接设 owner_write 可能被静默忽略。

  • 修改前先获取当前权限:auto old_perms = std::filesystem::status(path).permissions(),再按位操作,避免覆盖执行位等意外状态
  • Windows 下想禁用写入,请用 std::filesystem::perms::owner_write | std::filesystem::perms::group_write | std::filesystem::perms::others_write,然后 & ~ 写权限位;但注意:这等价于设置只读属性,而非 Unix 式 chmod
  • 批量修改时别用递归迭代器顺手改权限——std::filesystem::permissions() 对目录生效后,新创建文件会继承父目录默认权限(umask 影响),但已有子项不受影响;要真正递归改,得自己遍历并逐个调用
  • 权限修改失败时,std::filesystem::permissions()std::filesystem::filesystem_error,错误码可能是 std::errc::operation_not_permitted(Windows 管理员权限缺失)或 std::errc::read_only_file_system(挂载为 ro)

性能关键:什么时候该关掉递归遍历的 symlink 跟踪

默认情况下 recursive_directory_iterator 会跟随符号链接进入目标目录,导致重复遍历、无限循环甚至溢出。除非你明确需要解析软链内容,否则必须关掉。

立即学习C++免费学习笔记(深入)”;

  • 构造时传 std::filesystem::directory_options::none:这是最安全的默认选项,完全不跟踪 symlink
  • 如果只想跳过 symlink(不报错也不进入),用 std::filesystem::directory_options::skip_permission_denied | std::filesystem::directory_options::follow_directory_symlink 是错的——follow_directory_symlink 正是你要禁用的行为
  • 检测 symlink 用 entry.is_symlink(),它不触发访问,开销极低;可据此做白名单式放行(例如只允许进入特定可信路径下的 symlink)
  • 在 SSD 上遍历百万级小文件时,关闭 symlink 跟踪可减少 15%+ 的系统调用次数;配合 std::filesystem::directory_options::skip_permission_denied,整体耗时下降明显

实际递归查找并设只读的最小可行代码片段:

void find_and_chmod_ro(const std::filesystem::path& root, const std::string& ext) {     std::error_code ec;     for (std::filesystem::recursive_directory_iterator iter(root, std::filesystem::directory_options::skip_permission_denied, ec), end; iter != end && !ec; ++iter) {         if (ec) break;         auto& entry = *iter;         if (entry.is_regular_file() && entry.path().extension() == ext) {             try {                 std::filesystem::permissions(entry.path(),                     std::filesystem::perms::owner_read | std::filesystem::perms::group_read | std::filesystem::perms::others_read,                     std::filesystem::perm_options::replace);             } catch (const std::filesystem::filesystem_error& e) {                 // 忽略权限修改失败,继续下一个             }         }     } }

Windows 下测试时记得以管理员身份运行,否则对 Program Files 类路径的 permissions() 调用大概率失败;Linux 下则要注意 umask 是否干扰最终权限位。

text=ZqhQzanResources