**题目:如何使用C++17的std::filesystem遍历目录并统计文件大小?**

C++17 引入了 std::filesystem,它提供了跨平台的文件系统操作功能。利用该库,我们可以轻松实现遍历指定目录下的所有文件、子目录,并统计它们的大小。下面给出一个完整示例,并对关键步骤进行逐行说明,帮助你快速掌握该技术。


一、准备工作

  1. 编译器支持

    • GCC 8.1+、Clang 7.0+、MSVC 2017+ 都已支持 std::filesystem
    • 需要在编译命令中链接 stdc++fs(GCC 8.1以下)或 -lstdc++fs,GCC 9.1+ 与 Clang 7.0+ 已默认链接。
  2. 包含头文件

    #include <iostream>
    #include <filesystem>
    #include <iomanip>
  3. 命名空间简写(可选)

    namespace fs = std::filesystem;

二、核心代码

int main(int argc, char* argv[]) {
    // 1. 解析命令行参数
    if (argc != 2) {
        std::cerr << "用法: " << argv[0] << " <目录路径>\n";
        return 1;
    }
    fs::path targetDir(argv[1]);

    // 2. 判断路径是否存在且为目录
    if (!fs::exists(targetDir) || !fs::is_directory(targetDir)) {
        std::cerr << "错误:指定路径不存在或不是目录。\n";
        return 1;
    }

    std::uintmax_t totalSize = 0;        // 累计总大小
    std::size_t   fileCount = 0;         // 文件计数
    std::size_t   dirCount  = 0;         // 子目录计数

    // 3. 递归遍历目录
    for (auto const& dirEntry : fs::recursive_directory_iterator(targetDir)) {
        try {
            if (dirEntry.is_regular_file()) {
                ++fileCount;
                totalSize += dirEntry.file_size();  // 文件大小(字节)
            } else if (dirEntry.is_directory()) {
                ++dirCount;
            }
        } catch (const fs::filesystem_error& e) {
            // 处理访问错误(权限、符号链接失效等)
            std::cerr << "访问错误:" << e.what() << '\n';
        }
    }

    // 4. 输出结果
    std::cout << std::fixed << std::setprecision(2);
    std::cout << "目录统计信息:\n";
    std::cout << "  总文件数   :" << fileCount << '\n';
    std::cout << "  子目录数   :" << dirCount << '\n';
    std::cout << "  总文件大小:" << totalSize / (1024.0 * 1024.0) << " MB\n";

    return 0;
}

代码要点说明

步骤 说明
1. 解析命令行参数 让程序可接受目录路径作为参数,避免硬编码。
2. 路径校验 fs::exists 检查路径是否存在,fs::is_directory 确认为目录。
3. 递归遍历 fs::recursive_directory_iterator 自动处理子目录;使用 is_regular_file()is_directory() 判断类型。
4. 统计文件大小 file_size() 返回字节数;在循环中累加。
5. 错误处理 try-catch 捕获 filesystem_error,防止因权限或无效符号链接导致程序崩溃。
6. 输出结果 将总大小转换为 MB 并保留两位小数,使用 std::fixedstd::setprecision

三、进阶功能

  1. 过滤文件类型

    if (dirEntry.path().extension() == ".cpp") { /* 统计 .cpp 文件 */ }
  2. 忽略符号链接

    if (dirEntry.is_symlink()) continue;
  3. 多线程并行遍历

    • 使用 std::asynctbb::parallel_for_each,将目录拆分为子目录并行处理。
    • 注意对 totalSizefileCount 等共享变量使用互斥锁或原子操作。
  4. 展示进度条

    • 通过 std::chrono 记录耗时,并在 std::cout 中更新进度。

四、常见问题

问题 解决办法
编译错误:‘filesystem’ 未定义 确认使用的编译器版本并开启 C++17 标准 (-std=c++17)。
运行时出现 Permission denied 检查目录权限,或使用 fs::status 判断是否可读。
统计结果异常大 确认是否对同一文件统计多次,可能因符号链接导致。

五、总结

  • std::filesystem 极大简化了文件系统操作,提供了安全、跨平台的 API。
  • 通过递归迭代器,可轻松实现目录遍历与文件统计。
  • 在实际项目中,可进一步扩展功能,如按文件类型分组、按时间戳筛选、并行处理等,以满足更复杂的需求。

掌握这些基础后,你就能在 C++ 项目中高效地处理文件系统任务,为后续的日志分析、资源管理等功能打下坚实基础。

发表评论