跳到主要内容

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);
}
}
}

参考资料