5 - 文件系统支持库
C++标准库包含一个文件系统支持库,定义在<filesystem>
中,并且位于std::filesystem
名称空间中。它允许编写可移植的代码来处理文件系统,例如查询某路径是目录(Dir)还是文件(File),遍历路径内容,检索文件信息等操作。
文件系统支持库
路径(path)
路径可以是绝对的,也可以是相对的,并且可以包含文件名。下例定义了几个路径:
filesystem::path p1 {R"(D:\Foo\Bar)"}; // 这里使用原始字符串, 防止转义
filesystem::path p2 {"D:/Foo/Bar"};
filesystem::path p3 {R"(..\SomeFolder)"};
可以用.c_str()
, .native()
, 将路径插入流等形式输出这些路径:
cout << p1 << endl;
cout << p2 << endl;
// 在支持正反斜杠的Windows上的输出:
"D:\\Foo\\Bar"
"D:/Foo/Bar"
可以对路径进行一些修改操作:
添加分隔符的修改:
使用
.append()
或operator/=
可以将额外组件添加到路径中,并插入分隔符。filesystem::path p1 {R"(D:\Foo)"}; p1.append("Bar1"); p1 /= "Bar2"; cout << p1 << endl; // 在支持正反斜杠的Windows上的输出: "D:\\Foo\\Bar1\\Bar2"
不添加分隔符的修改:
使用
.concat()
或operator+=
将字符串连接到现有路径,不会插入路径分隔符。filesystem::path p1 {R"(D:\Foo)"}; p1.concat("Bar1"); p1 += "Bar2"; cout << p1 << endl; // 在支持正反斜杠的Windows上的输出: "D:\\FooBar1Bar2"
如果想要遍历路径上的每个组件,可以用基于范围的for
循环:
filesystem::path p1 {R"(D:\Foo\Bar)"};
for (const auto& component : p1)
{
cout << component << endl;
}
// 在支持正反斜杠的Windows上的输出:
"D:"
"\\"
"Foo"
"Bar"
除此之外,路径还支持以下操作:
filesystem::path p1 {R"(D:\Foo\Bar\target.rar)"};
cout << p1.root_name() << endl;
cout << p1.filename() << endl;
cout << p1.stem() << endl;
cout << p1.extension() << endl;
// 在支持正反斜杠的Windows上的输出:
"D:"
"target.rar"
"target"
".rar"
更多操作详见path 类 | Microsoft Learn。
目录条目(directory_entry)
有了路径后,我们得要访问它,因此需要构造一个directory_entry
。它支持exists()
,is_directory()
,is_regular_file()
,file_size()
,last_write_time()
等操作。
其中查询文件大小的示例如下:
filesystem::path path {"C:/windows/win.ini"};
filesystem::directory_entry dirEntry {path};
if (dirEntry.exists() && dirEntry.is_regular_file())
{
cout << "File size: " << dirEntry.file_size() << endl;
}
// 输出:
File size: 92
更多操作详见directory_entry 类 | Microsoft Learn。
辅助函数
<filesystem>
头文件还定义了一些辅助函数,方便我们进行文件操作。例如,可以用copy()
复制文件或目录;create_directory()
在文件系统上创建一个新目录;exists()
用来查询给定目录/文件是否存在;file_size()
获取一个文件的大小;last_write_time()
获取文件最后修改时间;space()
用来查询文件系统上的可用空间等。
例如,下例输出了文件系统的容量和剩余空间的大小:
filesystem::space_info info {filesystem::space("C:")};
cout << "Capacity: " << info.capacity << endl;
cout << "Free: " << info.free << endl;
// 输出:
Capacity: 214748360704
Free: 28322189312
更多操作详见<filesystem>
函数 | Microsoft Learn。
目录遍历
如果希望递归遍历给定目录中的所有文件和子目录,可以使用recursive_directory_iterator
:
using std::filesystem;
void printDirStructure(const path& p)
{
if (!exists(p)) { return; }
// 创建起始&结束递归迭代器
recursive_directory_iterator begin {p};
recursive_directory_iterator end {};
for (auto iter {begin}; iter != end; ++iter)
{
// 为树状格式化准备的空格间距
const string spacer(iter.depth() * 2,' ');
// 需要解引用以访问directory_entry
auto& entry {*iter};
if (is_regular_file(entry))
{
cout << format("{}File: {} ({} bytes)", spacer,
entry.path().string(), file_size(entry)) << endl;
} else if (is_directory(entry))
{
cout << format("{}Dir: {}", spacer, entry.path().string()) << endl;
}
}
}
也能使用directory_iterator
迭代目录内容,但需要手动实现递归:
using std::filesystem;
void printDirSturcture(const path& p, size_t level = 0)
{
if (!exists(p)) { return; }
const string spacer(level * 2,' ');
if (is_regular_file(p))
{
cout << format("{}File: {} ({} bytes)", spacer,
p.string(), file_size(p)) << endl;
} else if (is_directory(entry))
{
cout << format("{}Dir: {}", spacer, p.string()) << endl;
for (auto& entry : directory_iterator {p})
{
printDirStructure(entry, level + 1);
}
}
}
参考资料
- 飘零的落花 - 现代C++详解
- C++20高级编程