【问题标题】:Cross platform file list using wildcard使用通配符的跨平台文件列表
【发布时间】:2020-05-22 16:07:47
【问题描述】:

我正在寻找一个跨平台函数,它支持目录内容的通配符列表,类似于 windows 上的 FindFirstFile。

windows 中接受的通配符模式是否非常特定于 windows?我想要一些支持 FindFirstFile 通配符模式的东西,但他也在 Linux 中工作。

【问题讨论】:

  • man 3 glob 适合你
  • en.cppreference.com/w/cpp/filesystem - 这是您的标准提供的跨平台界面。
  • 但它不做通配符匹配@JesperJuhl
  • 不只是 glob Linux @S.M.
  • 我要求一些跨平台的东西,不涉及为操作系统放置 ifdefs

标签: c++ std-filesystem


【解决方案1】:

如果 C++17 及更高版本: 您可以使用目录迭代器“遍历”目录,并将遍历的文件名与正则表达式匹配,如下所示:

static std::optional<std::string> find_file(const std::string& search_path, const std::regex& regex) {
    const std::filesystem::directory_iterator end;
    try {
        for (std::filesystem::directory_iterator iter{search_path}; iter != end; iter++) {
            const std::string file_ext = iter->path().extension().string();
            if (std::filesystem::is_regular_file(*iter)) {
                if (std::regex_match(file_ext, regex)) {
                    return (iter->path().string());
                }
            }
        }
    }
    catch (std::exception&) {}
    return std::nullopt;
}

例如,用于查找以 .txt 结尾的第一个文件:

auto first_file = find_file("DocumentsDirectory", std::regex("\\.(?:txt)"));

同样,如果你感兴趣的不仅仅是扩展匹配,功能行

const std::string file_ext = iter->path().extension().string();

应修改为捕获您感兴趣的文件名部分(或文件的整个路径)的内容

这可以在一个函数中使用,该函数按目录执行通配符列表。

【讨论】:

  • 问题是接受的通配符与正则表达式不同。例如你可以做ls a*
  • 通配符的具体格式未连接到底层文件系统。 “ls”是一个系统工具,不应该期望 c++、语言(或任何操作系统)支持通配符翻译。但是,我认为没有什么能阻止您使用与 std::filesystem 匹配的正则表达式将该行为转换为您想要的任何可接受的通配符。
【解决方案2】:

这是一个递归变体。

它为列表中的每个文件调用一个函数 f 并返回找到的文件数。 它也是递归的:它将子目录下降到指定的最大深度。 请注意,搜索过滤器会匹配文件名。

删除了 try-catch 块,以便调用者可以捕获和处理任何问题。

#include <string>
#include <regex>
#include <filesystem>

// recursively call a functional *f* for each file that matches the expression
inline int foreach_file(const std::string& search_path, const std::regex& regex, int depth, std::function<void(std::string)> f) {
    int n = 0;
    const std::filesystem::directory_iterator end;
    for (std::filesystem::directory_iterator iter{ search_path }; iter != end; iter++) {
        const std::string filename = iter->path().filename().string();
        if (std::filesystem::is_regular_file(*iter)) {
            if (std::regex_match(filename, regex)) {
                n++;
                f(iter->path().string());
            }
        }
        else if (std::filesystem::is_directory(*iter) && depth>0) {
            n += foreach_file(iter->path().string(), regex, depth - 1, f);
        }
    }
    return n;
}

例子:


void do_something(string filename) {
...
}


void do_all_json_that_start_with_z() {
    // regex matches the whole filename 
    regex r("z.*.json", regex::ECMAScript | regex::icase); // ignoring case
    foreach_file(R"(C:\MyFiles\)", r, 99, do_something); // max depth 99
}

// can use lambdas
void do_all_json_that_start_with_z() {
    int n=0;
    foreach_file(
        R"(C:\MyFiles\)", // using raw string - for windows
        regex("z.*.json"),
        0,                // do not descend to sub-directories 
        [&n](string s) { printf("%d) %s\n", ++n, s.c_str()); });
}



【讨论】:

    猜你喜欢
    • 2011-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多