如何使用C++17的std::filesystem实现跨平台文件遍历?

在现代C++(自C++17起)中,std::filesystem 提供了一套高层次且跨平台的文件系统操作接口。它封装了底层的POSIX、Windows API,并提供了统一的语义,使得文件遍历、属性查询、路径操作等变得异常简单。下面给出一个完整的示例,演示如何:

  1. 递归遍历指定目录
  2. 过滤特定文件类型
  3. 获取文件大小与修改时间
  4. 异常安全的错误处理
#include <iostream>
#include <filesystem>
#include <chrono>
#include <iomanip>

namespace fs = std::filesystem;

// 格式化时间戳为可读字符串
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);
    char buffer[64];
    std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm);
    return std::string(buffer);
}

// 递归遍历,收集符合条件的文件信息
void traverse(const fs::path& dir, const std::string& ext_filter = "") {
    if (!fs::exists(dir) || !fs::is_directory(dir)) {
        std::cerr << "路径不存在或不是目录: " << dir << '\n';
        return;
    }

    std::cout << "遍历目录: " << dir << "\n\n";

    for (auto const& entry : fs::recursive_directory_iterator(dir)) {
        try {
            if (entry.is_regular_file()) {
                const auto& p = entry.path();
                if (!ext_filter.empty() && p.extension() != ext_filter)
                    continue;   // 过滤后缀

                auto fsize = fs::file_size(p);
                auto ftime = fs::last_write_time(p);
                std::cout << std::left << std::setw(30) << p.string() << std::right << std::setw(10) << fsize << " bytes" << " | 修改时间: " << format_time(ftime) << '\n';
            }
        } catch (const std::exception& e) {
            std::cerr << "读取文件信息失败: " << entry.path() << ",错误: " << e.what() << '\n';
        }
    }
}

int main() {
    std::string target_dir;
    std::cout << "请输入要遍历的目录路径: ";
    std::getline(std::cin, target_dir);

    std::string ext;
    std::cout << "输入想要过滤的文件后缀(可空,如 .cpp),或直接回车查看全部: ";
    std::getline(std::cin, ext);

    try {
        traverse(fs::path(target_dir), ext);
    } catch (const std::exception& e) {
        std::cerr << "遍历过程中发生未捕获异常: " << e.what() << '\n';
    }

    return 0;
}

关键点解析

主题 说明
std::filesystem 头文件 只需包含 `
`,C++17 标准库即支持。
路径对象 (fs::path) 该类型可跨平台使用 \\/,并支持诸如 string(), filename(), parent_path() 等成员。
递归遍历 (recursive_directory_iterator) 自动处理子目录,且提供 is_directory(), is_regular_file() 等检测。
异常安全 fs::recursive_directory_iterator 在遇到错误时会抛出 std::filesystem::filesystem_error,在循环内部可以捕获并继续处理。
文件属性 file_size(), last_write_time() 等成员函数提供常见信息。
时间格式化 由于 last_write_time() 返回的是 file_time_type,需要通过 chrono 转换为 time_t 再格式化。

适用场景

  • 构建工具:如自定义 make,需要扫描源码文件。
  • 文件同步:比较目录树差异。
  • 日志/报告生成:列出某目录下所有文件的大小和修改时间。
  • 安全扫描:找出未授权文件或旧文件。

通过上述示例,你可以快速上手 std::filesystem 并在自己的项目中加入高效、可维护的文件系统操作。祝编码愉快!

发表评论