如何使用C++17的std::filesystem实现递归遍历目录?

在C++17标准中,std::filesystem库为文件系统操作提供了统一的接口。下面展示了一个完整的示例,演示如何利用 std::filesystem::recursive_directory_iterator 对指定目录进行递归遍历,并打印出所有文件与子目录的路径、大小以及最后修改时间。示例代码可直接复制到任何支持C++17的编译器中运行。

#include <iostream>
#include <iomanip>
#include <filesystem>
#include <chrono>
#include <sstream>

namespace fs = std::filesystem;

// 将std::filesystem::file_time_type转换为可读的字符串
std::string format_time(const fs::file_time_type& ftime) {
    auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
        ftime - fs::file_time_type::clock::now()
        + std::chrono::system_clock::now());
    std::time_t tt = std::chrono::system_clock::to_time_t(sctp);
    std::tm tm = *std::localtime(&tt);
    std::ostringstream oss;
    oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
    return oss.str();
}

void print_directory(const fs::path& root) {
    if (!fs::exists(root) || !fs::is_directory(root)) {
        std::cerr << "错误: 目录不存在或不是目录。\n";
        return;
    }

    std::cout << "递归遍历目录: " << fs::absolute(root) << '\n';
    std::cout << std::left << std::setw(40) << "路径" << std::setw(12) << "大小 (字节)" << std::setw(20) << "最后修改时间" << '\n';
    std::cout << std::string(72, '-') << '\n';

    for (const auto& entry : fs::recursive_directory_iterator(root)) {
        try {
            std::string path = fs::absolute(entry.path()).string();
            std::uintmax_t size = 0;
            if (fs::is_regular_file(entry.status())) {
                size = fs::file_size(entry);
            }
            std::string mtime = format_time(fs::last_write_time(entry));

            std::cout << std::left << std::setw(40) << path << std::setw(12) << size << std::setw(20) << mtime << '\n';
        } catch (const fs::filesystem_error& e) {
            std::cerr << "访问文件时出错: " << e.what() << '\n';
        }
    }
}

int main(int argc, char* argv[]) {
    fs::path dir = ".";
    if (argc > 1) {
        dir = argv[1];
    }
    print_directory(dir);
    return 0;
}

代码说明

  1. 命名空间别名

    namespace fs = std::filesystem;

    简化后续使用。

  2. 时间格式化
    format_time 函数将 file_time_type 转换为 std::time_t,再用 std::put_time 生成易读字符串。此处做了时钟转换以兼容不同实现。

  3. 递归遍历

    for (const auto& entry : fs::recursive_directory_iterator(root)) { … }

    recursive_directory_iterator 自动深入子目录。若你想控制深度或排除某些路径,可使用 fs::directory_options::skip_permission_denied 或自行过滤。

  4. 错误处理
    访问文件属性可能抛出 filesystem_error,使用 try-catch 捕获并打印。

  5. 主函数
    允许通过命令行参数指定根目录;若未提供,默认当前目录。

运行示例

$ g++ -std=c++17 -o dirwalk dirwalk.cpp
$ ./dirwalk /path/to/your/project

程序会输出类似以下内容:

递归遍历目录: /path/to/your/project
路径                                       大小 (字节)   最后修改时间
--------------------------------------------------------------------
/path/to/your/project/main.cpp             2156          2024-01-15 10:12:03
/path/to/your/project/include/util.h       342           2024-01-15 09:58:17
/path/to/your/project/src/utils.cpp         987           2024-01-15 10:00:45
...

进阶使用

  • 过滤文件:在循环内判断 entry.path().extension() 或使用正则表达式过滤。
  • 并发遍历:将 recursive_directory_iterator 与线程池结合,提升大目录的遍历性能。
  • 属性统计:收集文件数、总大小、平均文件大小等统计信息。

以上示例展示了 std::filesystem 在递归遍历目录时的简洁性与强大功能。通过少量代码即可完成完整的文件系统扫描任务,为日志分析、备份工具、资源监测等场景提供便利。

发表评论