Reputation: 1019
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
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
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