C++17 中的 std::filesystem 简单使用与案例

随着 C++17 的发布,标准库新增了一个强大的文件系统库——std::filesystem。它提供了对文件与目录的创建、删除、遍历、属性查询等操作的统一接口,极大地方便了跨平台的文件处理工作。本文将从基础使用入手,演示常见操作,并结合实际案例展示如何使用该库完成一个简易的文件备份工具。

1. 头文件与命名空间

#include <filesystem>
namespace fs = std::filesystem;
  • `#include `:引入文件系统相关类型与函数。
  • namespace fs = std::filesystem;:为简化代码,常用的做法是使用别名 fs

注意:在 GCC 8 之前的版本,std::filesystem 处于实验性质,需加 -std=gnu++17 并链接 -lstdc++fs。在较新编译器(GCC 9+、Clang 10+、MSVC 19.20+)已稳定。

2. 基础操作

2.1 检查路径是否存在

fs::path p = "/usr/local/bin";
if (fs::exists(p)) {
    std::cout << p << " exists.\n";
}

2.2 判断文件或目录

if (fs::is_regular_file(p))   // 普通文件
if (fs::is_directory(p))     // 目录

2.3 创建目录

fs::path dir = "logs";
fs::create_directory(dir);           // 只创建单层目录
fs::create_directories(dir / "2026"); // 递归创建多层目录

2.4 读取目录

for (const auto &entry : fs::directory_iterator(dir)) {
    std::cout << entry.path() << '\n';
}

2.5 复制、移动、删除

fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
fs::rename(src, dst);
fs::remove_all(dir); // 删除目录及其内容

3. 读取文件属性

auto ftime = fs::last_write_time(p);
auto sz = fs::file_size(p);

last_write_time 返回一个 file_time_type,可以使用 std::chrono 进行转换。

4. 实战案例:简易文件备份工具

下面给出一个完整示例,演示如何使用 std::filesystem 复制源目录下的所有文件到目标备份目录,且只复制最近修改时间超过一天的文件。

#include <filesystem>
#include <iostream>
#include <chrono>

namespace fs = std::filesystem;

// 判断文件是否超过阈值(单位:天)
bool isModifiedAfter(const fs::path& p, int days) {
    auto ftime = fs::last_write_time(p);
    auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
        ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now()
    );
    auto now = std::chrono::system_clock::now();
    return (now - sctp) > std::chrono::hours(24 * days);
}

int main(int argc, char* argv[]) {
    if (argc != 3) {
        std::cerr << "Usage: backup <source_dir> <backup_dir>\n";
        return 1;
    }

    fs::path srcDir = argv[1];
    fs::path dstDir = argv[2];

    if (!fs::exists(srcDir) || !fs::is_directory(srcDir)) {
        std::cerr << "Source directory does not exist.\n";
        return 1;
    }

    fs::create_directories(dstDir); // 确保目标目录存在

    for (auto& entry : fs::recursive_directory_iterator(srcDir)) {
        if (fs::is_regular_file(entry.path())) {
            if (isModifiedAfter(entry.path(), 1)) {
                fs::path relative = fs::relative(entry.path(), srcDir);
                fs::path dest = dstDir / relative;
                fs::create_directories(dest.parent_path()); // 递归创建子目录
                fs::copy_file(entry.path(), dest, fs::copy_options::overwrite_existing);
                std::cout << "Backed up: " << entry.path() << " -> " << dest << '\n';
            }
        }
    }

    std::cout << "Backup completed.\n";
    return 0;
}

说明

  1. 递归遍历fs::recursive_directory_iterator 能遍历子目录。
  2. 相对路径fs::relative 计算源文件相对源根目录的路径,保证备份目录结构一致。
  3. 日期判断isModifiedAfter 将文件时间转换为系统时间,计算与当前时间差。
  4. 创建子目录:在复制前确保目标子目录已存在。

5. 性能与跨平台注意事项

  • 性能std::filesystem 在 I/O 密集型操作中与传统 boost::filesystem 相比,性能相当甚至略有提升。
  • Unicode:Windows 的 std::filesystem::path 在 UTF‑8 代码页下默认使用 UTF‑16 内部表示,读取时会自动转换。
  • 错误处理:使用 std::error_codetry-catch 捕获异常。示例中使用默认异常模式,若不想抛异常可使用 fs::remove_all(p, ec) 之类的 API。

6. 结语

std::filesystem 为 C++ 提供了现代、跨平台的文件操作方式,减少了繁琐的系统调用与第三方库。只要掌握了它的基本使用,几乎可以覆盖日常开发中所有的文件与目录处理需求。希望本文能帮助你在项目中快速上手,提升开发效率。

发表评论