C++17 中的 std::filesystem 简介

在 C++17 标准中,std::filesystem 库被正式纳入标准库,为文件和目录操作提供了统一、跨平台的接口。相比旧时的 POSIX API 或 Boost.Filesystem,std::filesystem 更易使用且与 C++ 语言特性深度融合。下面从概念、核心类型、常见操作、性能考虑以及实际应用案例四个方面进行系统阐述。


一、核心概念与命名空间

  • 命名空间namespace std::filesystem,可简写为 namespace fs,常用别名。
  • 路径对象fs::path,封装文件系统路径,支持字符串、字符宽度、以及拼接、切分等操作。
  • 文件/目录状态fs::file_statusfs::directory_entry,分别代表文件元信息与目录项。

二、主要功能模块

模块 主要函数/类型 说明
路径操作 fs::path, operator/, operator/= 路径拼接、获取扩展名、父目录等
文件系统遍历 fs::directory_iterator, fs::recursive_directory_iterator 迭代器式遍历,支持递归
文件属性 fs::status, fs::symlink_status, fs::file_type, fs::permissions 查询文件类型、权限、时间戳
文件操作 fs::copy, fs::rename, fs::remove, fs::remove_all, fs::create_directory, fs::create_directories, fs::create_symlink 常见文件系统操作
字符串编码 fs::u8path, fs::u16path, fs::u32path 处理 UTF-8/UTF-16/UTF-32 路径
错误处理 fs::filesystem_error 统一异常类型,包含 std::error_code

三、典型使用案例

1. 递归遍历并打印所有文件

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main() {
    fs::path root = "./src";
    try {
        for (const auto& entry : fs::recursive_directory_iterator(root)) {
            std::cout << entry.path() << '\n';
        }
    } catch (const fs::filesystem_error& e) {
        std::cerr << "Error: " << e.what() << '\n';
    }
}

2. 复制文件并保持权限

fs::copy(src, dst, fs::copy_options::overwrite_existing | fs::copy_options::update_existing);

3. 读取文件修改时间并格式化

auto ftime = fs::last_write_time(file);
auto sctp = std::chrono::system_clock::to_time_t(
               std::chrono::file_clock::to_sys(ftime));
std::cout << std::put_time(std::localtime(&sctp), "%F %T") << '\n';

四、性能与实现细节

  1. 延迟异常:默认 std::filesystem 在异常模式下会抛出 fs::filesystem_error,但也可以通过 std::error_code 方式获取错误信息,避免异常开销。
  2. 缓存机制:在递归遍历时,fs::directory_iterator 会在每次调用 ++ 时重新读取目录条目,避免显式缓存;若对性能敏感,可自行缓存。
  3. 跨平台差异:Windows 上 fs::path 默认使用 UTF-16(std::wstring),Linux/macOS 使用 UTF-8。使用 fs::u8path 可强制统一为 UTF-8。

五、实战场景

  1. 构建系统:在编译器或脚本中,使用 fs::recursive_directory_iterator 搜索 .cpp/.h,生成依赖关系。
  2. 日志归档:每天将日志文件复制到时间戳文件夹,使用 fs::create_directories 自动创建多层目录。
  3. 文件同步工具:比较源与目标的 last_write_timefile_size,决定是否拷贝。

六、常见坑与最佳实践

  • 不要滥用 fs::copyoverwrite_existing:若目标文件正被其他进程使用,拷贝会抛异常;可先尝试 remove_all 再复制。
  • 符号链接注意:默认 fs::copy 会复制链接本身;若想复制链接所指向的文件,使用 fs::copy_options::copy_symlinks
  • 权限保持:Windows 与 POSIX 的权限模型不同,fs::permissions 仅在 POSIX 下有效;Windows 使用 ACL,需要额外处理。

七、总结

std::filesystem 的加入,使得 C++ 能够像脚本语言那样轻松处理文件系统。其 API 设计优雅,异常安全,且与现代 C++ 语言特性(如 std::error_codestd::filesystem::path)无缝集成。掌握核心类型与常用函数后,即可在项目中快速实现文件遍历、复制、删除等功能,为代码简洁性与可维护性奠定坚实基础。

发表评论