...

/

List Files in a Directory

List Files in a Directory

Learn to display the list files in a directory.

We'll cover the following...

The filesystem library provides a directory_entry class with directory-related information about a given path. We can use this to create useful directory listings.

How to do it

In this recipe, we create a directory listing utility A directory listing utility is a software tool or command-line program that displays the contents of a directory, providing information about files and subdirectories within the specified directory. using the information in the directory_entry class:
• We start with our namespace alias and formatter specialization for displaying path objects:

namespace fs = std::filesystem;
template<>
struct std::formatter<fs::path>:
std::formatter<std::string> {
template<typename FormatContext>
auto format(const fs::path& p, FormatContext& ctx) {
return format_to(ctx.out(), "{}", p.string());
}
};
  • The directory_iterator class makes it easy to list a directory:

int main() {
constexpr const char* fn{ "." };
const fs::path fp{fn};
for(const auto& de : fs::directory_iterator{fp}) {
cout << format("{} ", de.path().filename());
}
cout << '\n';
}

Output:

chrono Makefile include chrono.cpp working formatter
testdir formatter.cpp working.cpp
  • We can add command-line options to make this work, like Unix ls:

int main(const int argc, const char** argv) {
fs::path fp{ argc > 1 ? argv[1] : "." };
if(!fs::exists(fp)) {
const auto cmdname {
fs::path{argv[0]}.filename() };
cout << format("{}: {} does not exist\n", cmdname, fp);
return 1;
}
if(is_directory(fp)) {
for(const auto& de : fs::directory_iterator{fp}) {
cout << format("{} ", de.path().filename());
}
} else {
cout << format("{} ", fp.filename());
}
cout << '\n';
}

If there is a command-line argument, we use it to create a path object. Otherwise, we use "." for the current directory.

We check if the path exists with if_exists(). If not, we print an error message and exit. The error message includes cmdname from argv[0].

Next, we check is_directory(). If we have a directory, we loop through a directory_iterator for each entry. directory_iterator iterates over directory_entry objects. de.path().filename() gets the path and filename from each directory_entry object.

Output:

./working
chrono Makefile include chrono.cpp working formatter
testdir formatter.cpp working.cpp
./working working.cpp
working.cpp
./working foo.bar
working: foo.bar does not exist
  • If we want our output sorted, we can store our directory_entry objects in a sortable container.

Let's create an alias for fs::directory_entry. We'll be using this a lot. This goes at the top of the file:

using de = fs::directory_entry;

At the top of main(), we declare a vector of de objects:

vector<de> entries{};

Inside the is_directory() block, we load the vector, sort it, and then ...