第11.18章: 文件系统 API

English

概述

::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字符设备。
fifoFIFO(命名管道)。
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");
    }
}

要点