. 引言
在 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;
- 路径操作:相对路径与绝对路径
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 << "打开文件失败: " << filePath << std::endl;
return;
}
ofs.write(content.data(), content.size());
std::cout << "文件已创建: " << filePath << 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& entry : fs::directory_iterator(dirPath)) {
std::cout << (entry.is_directory() ? "[目录] " : "[文件] ")
<< entry.path().filename() << std::endl;
}
// 递归遍历所有层
std::cout << "--- 递归遍历 ---" << std::endl;
for (const auto& entry : fs::recursive_directory_iterator(dirPath)) {
std::cout << entry.path() << 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 << "文件大小: " << size << " 字节" << std::endl;
// 最后修改时间
auto mtime = fs::last_write_time(filePath);
std::time_t t = decltype(mtime)::clock::to_time_t(mtime);
std::cout << "最后修改: " << std::ctime(&t);
// 文件类型与权限
fs::file_status status = fs::status(filePath);
std::cout << "文件类型: ";
switch (status.type()) {
case fs::file_type::regular: std::cout << "普通文件"; break;
case fs::file_type::directory: std::cout << "目录"; break;
case fs::file_type::symlink: std::cout << "符号链接"; break;
default: std::cout << "其他"; break;
}
std::cout << std::endl;
// 权限判断
fs::perms p = status.permissions();
std::cout << "所有者可读: " << ((p & fs::perms::owner_read) != fs::perms::none) << std::endl;
std::cout << "所有者可写: " << ((p & fs::perms::owner_write) != fs::perms::none) << std::endl;
}