std::filesystem 文件操作详解

. 引言

在 C++ 开发中,文件与文件夹操作是绕不开的基础能力。早期我们通常依赖 C 语言的 fopen、stat,或者平台相关的 Win32 API、POSIX 接口,代码往往需要针对不同系统分别编写。C++17 引入的 std::filesystem 标准库,为跨平台文件系统操作提供了统一、现代且类型安全的解决方案。

本文参考我之前发布的 Qt 文件操作博客(Qt 文件操作实践),将其中涉及的文件、文件夹、信息获取、路径处理等操作,尽可能用 std::filesystem 重新实现一遍,方便读者对照学习两种技术栈的异同。

2. 环境准备与头文件

使用 std::filesystem 需要 C++17 及以上标准。在 CMake 中需要显式链接 stdc++fs(GCC 8 以下)或直接使用较新编译器自带的实现。

cpp 复制代码
#include <filesystem>
#include <iostream>
#include <fstream>
#include <string>
namespace fs = std::filesystem;
  1. 路径操作:相对路径与绝对路径

Qt 中我们常用 QDir::currentPath()、QFileInfo::absoluteFilePath() 等获取路径信息。在 std::filesystem 中,核心类型是 fs::path,它封装了路径的拼接、分解、规范化等操作。

3.1 获取当前路径与绝对路径

cpp 复制代码
// 获取当前工作目录
fs::path current = fs::current_path();
std::cout << "当前目录: " << current << std::endl;
// 相对路径转绝对路径
fs::path rel = "docs/readme.txt";
fs::path abs = fs::absolute(rel);
std::cout << "绝对路径: " << abs << std::endl;
// 规范化路径(去除 . 和 ..)
fs::path norm = fs::weakly_canonical(rel);
std::cout << "规范化路径: " << norm << std::endl;

3.2 路径分解与拼接

cpp 复制代码
fs::path p = "/home/user/project/src/main.cpp";
std::cout << "父路径: " << p.parent_path() << std::endl;
std::cout << "文件名: " << p.filename() << std::endl;
std::cout << "主干名: " << p.stem() << std::endl;
std::cout << "扩展名: " << p.extension() << std::endl;
// 路径拼接(推荐使用 / 运算符)
fs::path base = "/home/user/project";
fs::path full = base / "build" / "output.bin";
std::cout << "拼接结果: " << full << std::endl;

对照 Qt:QFileInfo::path()、fileName()、baseName()、suffix() 分别对应上述四个方法。

4. 文件操作:创建、复制、移动、删除

Qt 中对应 QFile::copy()、QFile::rename()、QFile::remove()。下面用 std::filesystem 实现同样的能力。

4.1 创建文件并写入内容

cpp 复制代码
void createFile(const fs::path& filePath, const std::string& content) {
    // 确保父目录存在
    fs::create_directories(filePath.parent_path());
    std::ofstream ofs(filePath, std::ios::binary);
    if (!ofs) {
      std::cerr &lt;&lt; "打开文件失败: " &lt;&lt; filePath &lt;&lt; std::endl;
      return;
     }
    ofs.write(content.data(), content.size());
    std::cout &lt;&lt; "文件已创建: " &lt;&lt; filePath &lt;&lt; std::endl;
}

4.2 复制文件

cpp 复制代码
void copyFile(const fs::path& src, const fs::path& dst) {
    try {
        // 如果目标已存在则覆盖
        fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
        std::cout << "复制成功: " << src << " -> " << dst << std::endl;
    } catch (const fs::filesystem_error& e) {
        std::cerr << "复制失败: " << e.what() << std::endl;
    }
}

4.3 移动(重命名)文件

cpp 复制代码
void moveFile(const fs::path& src, const fs::path& dst) {
    try {
        fs::rename(src, dst);
        std::cout << "移动成功: " << src << " -> " << dst << std::endl;
    } catch (const fs::filesystem_error& e) {
        std::cerr << "移动失败: " << e.what() << std::endl;
    }
}

4.4 删除文件

cpp 复制代码
void deleteFile(const fs::path& filePath) {
    if (!fs::exists(filePath)) {
        std::cout << "文件不存在: " << filePath << std::endl;
        return;
    }
    if (fs::remove(filePath)) {
        std::cout << "删除成功: " << filePath << std::endl;
    } else {
        std::cerr << "删除失败: " << filePath << std::endl;
    }
}

5. 文件夹操作:创建、遍历、复制、删除

Qt 中对应 QDir::mkdir()、QDir::entryList()、QDir::removeRecursively()。

5.1 创建文件夹

cpp 复制代码
void createDirectory(const fs::path& dirPath) {
    // 创建单层目录
    bool ok = fs::create_directory(dirPath);
    // 递归创建多层目录
    bool okRecursive = fs::create_directories(dirPath / "sub" / "deep");
    std::cout << "单层创建: " << (ok ? "成功" : "失败或已存在") << std::endl;
    std::cout << "递归创建: " << (okRecursive ? "成功" : "失败或已存在") << std::endl;
}

5.2 遍历文件夹

cpp 复制代码
void listDirectory(const fs::path& dirPath) {
    std::cout << "=== 遍历目录: " << dirPath << " ===" << std::endl;
// 仅遍历当前层
for (const auto&amp; entry : fs::directory_iterator(dirPath)) {
    std::cout &lt;&lt; (entry.is_directory() ? "[目录] " : "[文件] ")
              &lt;&lt; entry.path().filename() &lt;&lt; std::endl;
}

// 递归遍历所有层
std::cout &lt;&lt; "--- 递归遍历 ---" &lt;&lt; std::endl;
for (const auto&amp; entry : fs::recursive_directory_iterator(dirPath)) {
    std::cout &lt;&lt; entry.path() &lt;&lt; std::endl;
}
}

5.3 递归复制整个文件夹

cpp 复制代码
void copyDirectory(const fs::path& src, const fs::path& dst) {
    try {
        fs::copy(src, dst, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
        std::cout << "目录复制完成: " << src << " -> " << dst << std::endl;
    } catch (const fs::filesystem_error& e) {
        std::cerr << "目录复制失败: " << e.what() << std::endl;
    }
}

5.4 递归删除整个文件夹

cpp 复制代码
void deleteDirectory(const fs::path& dirPath) {
    try {
        // remove_all 递归删除目录及其所有内容
        std::uintmax_t count = fs::remove_all(dirPath);
        std::cout << "已删除 " << count << " 个文件/目录: " << dirPath << std::endl;
    } catch (const fs::filesystem_error& e) {
        std::cerr << "删除失败: " << e.what() << std::endl;
    }
}

6. 文件信息获取:大小、时间、权限

Qt 中对应 QFileInfo::size()、lastModified()、isReadable() 等。在 std::filesystem 中通过 fs::file_status 和 fs::file_time_type 实现。

cpp 复制代码
void getFileInfo(const fs::path& filePath) {
    if (!fs::exists(filePath)) {
        std::cout << "路径不存在: " << filePath << std::endl;
        return;
    }
// 文件大小(字节)
std::uintmax_t size = fs::file_size(filePath);
std::cout &lt;&lt; "文件大小: " &lt;&lt; size &lt;&lt; " 字节" &lt;&lt; std::endl;

// 最后修改时间
auto mtime = fs::last_write_time(filePath);
std::time_t t = decltype(mtime)::clock::to_time_t(mtime);
std::cout &lt;&lt; "最后修改: " &lt;&lt; std::ctime(&amp;t);

// 文件类型与权限
fs::file_status status = fs::status(filePath);
std::cout &lt;&lt; "文件类型: ";
switch (status.type()) {
    case fs::file_type::regular: std::cout &lt;&lt; "普通文件"; break;
    case fs::file_type::directory: std::cout &lt;&lt; "目录"; break;
    case fs::file_type::symlink: std::cout &lt;&lt; "符号链接"; break;
    default: std::cout &lt;&lt; "其他"; break;
}
std::cout &lt;&lt; std::endl;

// 权限判断
fs::perms p = status.permissions();
std::cout &lt;&lt; "所有者可读: " &lt;&lt; ((p &amp; fs::perms::owner_read) != fs::perms::none) &lt;&lt; std::endl;
std::cout &lt;&lt; "所有者可写: " &lt;&lt; ((p &amp; fs::perms::owner_write) != fs::perms::none) &lt;&lt; std::endl;
}
相关推荐
kaixin_learn_qt_ing1 小时前
[银河麒麟]因为没有安装串口相关驱动,导致无法检测到对应的串口,后来安装后,我的qt的相关接口就可以识别到了
qt
Nebula_g2 小时前
JavaSE加强:IO流
java·开发语言·windows·stream·javase
Ada's10 小时前
【计算机基础系列】003:Python数据结构
开发语言·数据结构·python
2601_9628857210 小时前
如何用 Python 计算 TRIX 三重指数平滑均线指标?
开发语言·python
还卿一钵无情泪11 小时前
Unsloth 微调 构建自己的大模型 没有GPU也能微调
linux·开发语言·人工智能·python·大模型·nlp·unsloth
倔强的石头10612 小时前
应用账号最小权限实践:读写账号、报表账号、运维账号分层
java·运维·开发语言
传人once12 小时前
页面中心圆圈放大效果如何写
开发语言·javascript·ecmascript
NeilYuen13 小时前
【C++】STL源码仿写(二):vector
开发语言·c++
蜗牛互联网13 小时前
Python消费Responses SSE事件:增量文本、超时与取消
java·开发语言·人工智能·后端·python