第11.18章: 文件系统 API
概述
::fast_io::dir_file 打开一个用于枚举的目录句柄。与 at()、current() 和 recursive() 结合使用时,它提供了一种强大且类型安全的文件系统遍历方式。每次迭代产生一个 directory_entry,带有查询文件名、扩展名和文件类型的函数。
打开目录
由路径构造一个 dir_file。构造函数接受任何能转换成 C 字符串的东西 — C 字符串、string_view、u8string,或者用 os_c_str 包装过的对象。
#include <fast_io.h>
#include <fast_io_device.h>
int main(int argc, char** argv) {
using namespace ::fast_io::iomnp;
// From a string literal (no wrapping needed)
::fast_io::dir_file df(u8"/home/user/docs");
// From a runtime C string (must wrap with os_c_str)
::fast_io::dir_file df2(::fast_io::mnp::os_c_str(argv[1]));
}
at() 函数
调用 at(df) 得到一个 at_entry — 锚定在该目录上的轻量句柄。current() 与 recursive() 用这个句柄枚举目录项,它也可以用来相对于该目录打开文件(如第11.1章所示)。
::fast_io::dir_file df(u8"my_directory");
auto entry = at(df); // at_entry anchored at my_directory
遍历直接子项:current()
current(at(df)) 返回一个 range,依次给出该目录的每个直接子项(不递归)。在 range-for 循环中使用它:
#include <fast_io.h>
#include <fast_io_device.h>
int main() {
using namespace ::fast_io::iomnp;
::fast_io::dir_file df(u8".");
for (auto const& ent : current(at(df)))
{
if (is_dot(ent)) continue; // skip . and ..
switch (type(ent)) {
using enum ::fast_io::file_type;
case directory: print("[DIR] "); break;
case symlink: print("[LINK] "); break;
case regular: print("[FILE] "); break;
default: print("[OTHER]");
}
println(::fast_io::mnp::code_cvt(u8filename(ent)));
}
}
递归遍历:recursive()
recursive(at(df)) 走遍该目录的所有后代,给出其中每一个文件与子目录。这非常适合搜索或处理整棵目录树。
#include <fast_io.h>
#include <fast_io_device.h>
int main() {
using namespace ::fast_io::iomnp;
::fast_io::dir_file df(u8"src");
::std::size_t cpp_count{};
::std::size_t total_lines{};
for (auto const& ent : recursive(at(df)))
{
if (type(ent) != ::fast_io::file_type::regular) continue;
::std::u8string_view ext{u8extension(ent)};
if (ext == u8".cpp" || ext == u8".h")
{
++cpp_count;
// Load the file and count lines
::fast_io::native_file_loader loader(drt(ent));
for (auto ch : loader)
{
if (ch == u8'\n') ++total_lines;
}
}
}
println("Found ", cpp_count, " C++ files with ", total_lines, " total lines");
}
目录项函数
current() 或 recursive() 给出的每个 directory_entry 都暴露这些函数:
| 函数 | 返回 | 说明 |
|---|---|---|
u8filename(ent) |
char8_t 的 string_view |
UTF-8 形式的文件名。 |
u8extension(ent) |
char8_t 的 string_view |
UTF-8 形式的扩展名(从最后一个 . 开始)。 |
u8stem(ent) |
char8_t 的 string_view |
UTF-8 形式的不含扩展名的文件名。 |
type(ent) |
file_type |
文件类型(见下文)。 |
is_dot(ent) |
bool |
名字是 . 或 .. 时为真。 |
drt(ent) |
fs_dirent |
一个 (fd, name) 对 — 传给文件构造函数或加载器即可打开该项。 |
at(ent) |
at_entry |
把 *at 风格的操作锚定到该目录项上。 |
此外还有 native_filename()、native_extension() 和 native_stem() 变体,返回原生编码形式的文件名(在所有平台上未必是 UTF-8)。
file_type 枚举
::fast_io::file_type 对文件系统项分类:
| 取值 | 说明 |
|---|---|
regular | 普通文件。 |
directory | 目录。 |
symlink | 符号链接。 |
block | 块设备。 |
character | 字符设备。 |
fifo | FIFO(命名管道)。 |
socket | 套接字。 |
unknown | 未知类型。 |
not_found | 文件不存在。 |
相对于目录项打开文件
用 drt(ent) 得到一个 (fd, name) 对,文件构造函数和加载器可以直接打开它。这比拼接完整路径更高效,也避免了路径解析带来的竞态。
#include <fast_io.h>
#include <fast_io_device.h>
int main() {
using namespace ::fast_io::iomnp;
::fast_io::dir_file df(u8"data");
for (auto const& ent : current(at(df)))
{
if (type(ent) != ::fast_io::file_type::regular) continue;
// Open the entry directly using drt(ent)
::fast_io::native_file_loader loader(drt(ent));
println(::fast_io::mnp::code_cvt(u8filename(ent)), ": ", loader.size(), " bytes");
}
}
要点
- 用
::fast_io::dir_file打开目录句柄。 - 调用
at(df)得到用于枚举的at_entry。 - 用
current(at(df))遍历直接子项(不递归)。 - 用
recursive(at(df))遍历所有后代。 - 每个目录项暴露
u8filename()、u8extension()、type()、is_dot()、drt()和at()。 - 用
drt(ent)高效地相对于目录项打开文件。 - 把
type(ent)与file_type枚举配合,按文件类型过滤。