在现代 C++ 开发中,文件系统操作往往是不可或缺的一环。传统上,程序员需要依赖平台特定的 API(如 Windows 的 FindFirstFile / POSIX 的 opendir)或者第三方库(如 Boost.Filesystem)来完成文件读写、遍历、复制等任务。随着 C++17 标准的发布,标准库新增了 <filesystem> 头文件,提供了一套统一、跨平台、类型安全的文件系统接口,极大简化了这类任务。
下面我们通过一个完整示例来演示如何利用 std::filesystem 完成以下操作:
- 递归遍历指定目录,打印所有文件路径;
- 复制文件或目录到目标位置;
- 检查文件是否存在、获取文件大小、修改时间等元数据;
- 创建、删除文件与目录;
- 处理错误与异常。
代码示例均以
cpp语法高亮,并可直接在支持 C++17 的编译器(如 GCC 8+、Clang 7+、MSVC 2017+)中编译运行。
#include <iostream>
#include <filesystem>
#include <fstream>
#include <chrono>
#include <iomanip>
namespace fs = std::filesystem;
// 1. 递归遍历目录
void print_all_files(const fs::path& dir) {
if (!fs::exists(dir) || !fs::is_directory(dir)) {
std::cerr << "错误: 目录不存在或不是目录: " << dir << '\n';
return;
}
for (const auto& entry : fs::recursive_directory_iterator(dir)) {
try {
std::cout << (entry.is_directory() ? "[DIR ] " : "[FILE] ") << entry.path() << '\n';
} catch (const fs::filesystem_error& e) {
std::cerr << "访问错误: " << e.what() << '\n';
}
}
}
// 2. 复制文件或目录
void copy_item(const fs::path& src, const fs::path& dst) {
try {
if (fs::is_directory(src)) {
fs::copy(src, dst, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
} else if (fs::is_regular_file(src)) {
fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
} else {
std::cerr << "未知文件类型: " << src << '\n';
return;
}
std::cout << "复制成功: " << src << " -> " << dst << '\n';
} catch (const fs::filesystem_error& e) {
std::cerr << "复制失败: " << e.what() << '\n';
}
}
// 3. 获取文件元数据
void print_file_info(const fs::path& p) {
try {
if (!fs::exists(p)) {
std::cout << "文件不存在: " << p << '\n';
return;
}
std::cout << "路径: " << p << '\n';
std::cout << "类型: ";
if (fs::is_regular_file(p)) std::cout << "普通文件\n";
else if (fs::is_directory(p)) std::cout << "目录\n";
else if (fs::is_symlink(p)) std::cout << "符号链接\n";
else std::cout << "其他\n";
std::cout << "大小: " << fs::file_size(p) << " 字节\n";
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());
std::time_t cftime = std::chrono::system_clock::to_time_t(sctp);
std::cout << "修改时间: " << std::put_time(std::localtime(&cftime), "%F %T") << '\n';
} catch (const fs::filesystem_error& e) {
std::cerr << "获取信息失败: " << e.what() << '\n';
}
}
// 4. 创建与删除
void create_and_remove_demo() {
fs::path tmp_dir = "demo_dir";
fs::path tmp_file = tmp_dir / "example.txt";
// 创建目录
try {
if (fs::create_directory(tmp_dir)) {
std::cout << "创建目录: " << tmp_dir << '\n';
} else {
std::cout << "目录已存在: " << tmp_dir << '\n';
}
// 写文件
std::ofstream ofs(tmp_file);
ofs << "Hello, std::filesystem!\n";
ofs.close();
std::cout << "写入文件: " << tmp_file << '\n';
} catch (const fs::filesystem_error& e) {
std::cerr << "创建失败: " << e.what() << '\n';
}
// 删除文件与目录
try {
if (fs::remove(tmp_file)) {
std::cout << "删除文件: " << tmp_file << '\n';
}
if (fs::remove(tmp_dir)) {
std::cout << "删除目录: " << tmp_dir << '\n';
}
} catch (const fs::filesystem_error& e) {
std::cerr << "删除失败: " << e.what() << '\n';
}
}
int main() {
std::cout << "=== 文件系统演示 ===\n\n";
// 1. 遍历当前目录
std::cout << "1. 递归遍历当前目录:\n";
print_all_files(fs::current_path());
std::cout << '\n';
// 2. 复制示例
std::cout << "2. 复制示例 (如果目录存在):\n";
if (fs::exists("src") && fs::is_directory("src")) {
copy_item("src", "dst");
} else {
std::cout << "源目录 'src' 不存在,跳过复制。\n";
}
std::cout << '\n';
// 3. 获取文件信息
std::cout << "3. 文件信息:\n";
print_file_info("demo.cpp"); // 替换为你自己的文件名
std::cout << '\n';
// 4. 创建与删除
std::cout << "4. 创建与删除演示:\n";
create_and_remove_demo();
return 0;
}
关键点说明
-
命名空间简化
namespace fs = std::filesystem;让代码更简洁。 -
递归遍历
fs::recursive_directory_iterator能自动遍历子目录;可以通过options参数控制是否遵循符号链接等。 -
复制
fs::copy与fs::copy_file的copy_options允许你覆盖已有文件、递归复制目录、保持权限等。 -
元数据
fs::file_size、fs::last_write_time、fs::is_regular_file等函数提供了文件属性的安全访问。注意last_write_time返回的是一个抽象时间点,需要转换为系统时间以便打印。 -
错误处理
std::filesystem抛出的filesystem_error包含了系统错误码,可通过e.code()获取std::error_code,进一步分析错误原因。 -
兼容性
只要编译器支持 C++17 并开启-std=c++17(或更高),上述代码即可在 Windows、Linux、macOS 等平台上编译运行。
小结
C++17 的 std::filesystem 提供了一套丰富、类型安全且跨平台的文件系统 API。无论是简单的文件读取、写入,还是复杂的目录遍历、复制、移动、权限管理,使用 std::filesystem 都能让代码更简洁、更可靠。熟练掌握它,能够显著提高日常项目的开发效率与代码可维护性。