Captain Jack sparrow
Captain Jack sparrow

Reputation: 1019

Cross platform file list using wildcard

I'm looking for a cross platform function that supports wildcard listing of a directory contents similar to what FindFirstFile on windows.

Is the wildcard pattern accepted in windows very specific to windows? I want something that supports FindFirstFile wildcard pattern but he working in Linux as well.

Upvotes: 1

Views: 4851

Answers (2)

QT-1
QT-1

Reputation: 996

Here is a recursive variant.

It calls a functional f for each file in the list and returns the number of files found. It is also recursive: it descends sub directories to the max depth specified. Note that the search filter does a filename is matched.

The try-catch block in removed so that the caller can catch and process any problems.

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

Example:


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



Upvotes: 0

bochko
bochko

Reputation: 383

If C++17 and above: You can "walk" a directory using a directory iterator, and match walked file names with a regex, like this:

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

Usage would be for example, for finding the first file, that ends in .txt:

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

Similarly, if you are interested in more than matching by extension, the function line

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

should be modified to something that captures the part of the filename you are interested in (or the whole path to the file)

This could then be used in a function, which performs the wildcard listing by directory.

Upvotes: 4

Related Questions