**如何使用C++17中的`std::filesystem`库进行跨平台文件操作**

在C++17标准中,STL新增了<filesystem>头文件,提供了一套统一的跨平台文件系统API。相比传统的<dirent.h>(POSIX)或<windows.h>(Windows)等专用库,std::filesystem可以让我们用同一套代码完成文件/目录的创建、删除、遍历、属性查询等操作,无需关心底层平台差异。

下面我们用一个完整的示例程序来演示:

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

namespace fs = std::filesystem;

// 用来格式化时间戳
std::string format_time(const fs::file_time_type& ftime)
{
    using namespace std::chrono;
    auto sctp = system_clock::to_time_t(ftime);
    std::tm tm{};
#if defined(_MSC_VER)
    localtime_s(&tm, &sctp);
#else
    localtime_r(&sctp, &tm);
#endif
    std::ostringstream oss;
    oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
    return oss.str();
}

// 递归遍历目录,返回所有文件路径
std::vector<fs::path> list_files(const fs::path& dir)
{
    std::vector<fs::path> files;
    if (!fs::exists(dir) || !fs::is_directory(dir))
        return files;

    for (auto const& entry : fs::recursive_directory_iterator(dir))
    {
        if (fs::is_regular_file(entry))
            files.push_back(entry.path());
    }
    return files;
}

int main()
{
    try
    {
        // 1. 创建目录
        fs::path dir = "demo_dir";
        if (!fs::exists(dir))
        {
            fs::create_directories(dir);
            std::cout << "目录创建: " << dir << '\n';
        }

        // 2. 创建文件
        fs::path file = dir / "hello.txt";
        std::ofstream ofs(file);
        ofs << "Hello, std::filesystem!\n";
        ofs.close();
        std::cout << "文件创建: " << file << '\n';

        // 3. 查询属性
        auto perms = fs::status(file).permissions();
        std::cout << "文件权限: ";
        std::cout << ((perms & fs::perms::owner_read) != fs::perms::none ? "r" : "-");
        std::cout << ((perms & fs::perms::owner_write) != fs::perms::none ? "w" : "-");
        std::cout << ((perms & fs::perms::owner_exec) != fs::perms::none ? "x" : "-") << '\n';

        // 4. 获取文件大小
        std::cout << "文件大小: " << fs::file_size(file) << " bytes\n";

        // 5. 获取修改时间
        std::cout << "上次修改时间: " << format_time(fs::last_write_time(file)) << '\n';

        // 6. 递归遍历
        std::cout << "目录下所有文件:\n";
        auto files = list_files(dir);
        for (auto const& f : files)
        {
            std::cout << "  " << f << '\n';
        }

        // 7. 删除文件
        fs::remove(file);
        std::cout << "文件已删除: " << file << '\n';

        // 8. 删除目录(空目录)
        fs::remove(dir);
        std::cout << "目录已删除: " << dir << '\n';
    }
    catch (const fs::filesystem_error& e)
    {
        std::cerr << "文件系统错误: " << e.what() << '\n';
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

代码说明

  1. 命名空间别名
    namespace fs = std::filesystem; 方便使用。

  2. 时间格式化
    std::filesystem::file_time_type 在不同平台内部是不同类型,需要先转换为 std::time_t 再格式化。示例兼容 MSVC 与 GCC/Clang。

  3. 递归遍历
    fs::recursive_directory_iterator 简单实现深度优先遍历。若只需要浅层遍历,可改为 fs::directory_iterator

  4. 错误处理
    所有文件系统操作可能抛出 std::filesystem_error,建议使用 try‑catch 捕获。

  5. 权限检查
    fs::perms 枚举支持位运算,可以快速判断可读/写/执行权限。

  6. 创建与删除
    fs::create_directories 能一次性创建多级目录。删除时 remove 仅能删除空目录或单个文件,若要递归删除文件夹请使用 fs::remove_all

常见陷阱

  • Windows 兼容:Windows 下面文件路径使用反斜杠 \,但 std::filesystem 允许使用正斜杠 /,并自动转换。若需要原始 Windows 路径,可使用 std::filesystem::path::generic_string()wstring
  • 编码:在 Windows 上,文件路径若包含非 ASCII 字符,最好使用 std::filesystem::path::wstring() 或在 wmain 程序中使用宽字符。
  • 符号链接fs::symlink_statusfs::status 区别;后者会跟随链接,前者不跟随。

结论

std::filesystem 是 C++17 之后统一的文件系统 API,几乎涵盖了所有常见需求,消除了手写平台差异代码的繁琐。掌握它可以让你的 C++ 项目在 Linux、macOS、Windows 上都能保持同一套代码,极大提升可维护性与开发效率。

发表评论