Directory Iterators

An introduction to iterating through the file system, using directory_iterator and recursive_directory_iterator.
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, Unlimited Access
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated

A common requirement we’ll have is to understand what files and directories exist in a specific location on the file system.

We can accomplish this using iterators that are provided within the std::filesystem namespace, which we’ll alias to fs in this lesson.

Using Directory Iterators

We can navigate through a directory using a fs::directory_iterator pair. By passing a fs::path to this type, we can define the directory we want to iterate through.

The default constructor for fs::directory_iterator returns a sentinel, which we can use to indicate when we’ve iterated through all the files and directories in the path. We cover iterators and sentinels in more detail in these lessons:

To use these iterators manually, we can increment our starting iterator until it becomes equal to the default-constructed sentinel:

#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;

int main() {
  fs::directory_iterator Start{R"(c:\test)"};  
  fs::directory_iterator End{};                

  for (auto Iter{Start}; Iter != End; ++Iter) {
    std::cout << Iter->path().string() << '\n';
  }
}
c:\test\file.txt
c:\test\hello.backup
c:\test\hello.txt
c:\test\subdirectory

Iterating Directories using Ranges

Instead of working with iterators directly, we can use them to create a subrange. We can then use this subrange to apply range-based techniques. Below, we iterate through a directory using a range-based for loop:

#include <filesystem>
#include <iostream>
#include <ranges>
namespace fs = std::filesystem;

int main() {
  using std::ranges::subrange;

  subrange DirectoryEntries{
    fs::directory_iterator{R"(c:\test)"}};

  for (const auto& Entry : DirectoryEntries) {
    std::cout << Entry.path().string() << '\n';
  }
}
c:\test\file.txt
c:\test\hello.backup
c:\test\hello.txt
c:\test\subdirectory

Below, we use a range-based algorithm (std::ranges::for_each) to output some additional information about the contents of our directory:

#include <filesystem>
#include <iostream>
#include <ranges>
namespace fs = std::filesystem;

void Log(const fs::directory_entry& Entry) {
  std::cout << Entry.path().string();

  if (Entry.is_directory()) {
    std::cout << " (Directory)\n";
  } else if (Entry.is_regular_file()) {
    std::cout << " ("
      << Entry.file_size() << " Bytes)\n";
  }
}

int main() {
  using std::ranges::subrange;

  std::ranges::for_each(
    fs::directory_iterator{R"(c:\test)"}, Log);
}
c:\test\file.txt (0 Bytes)
c:\test\hello.backup (24 Bytes)
c:\test\hello.txt (24 Bytes)
c:\test\subdirectory (Directory)

By default, the fs::directory_iterator only iterates through the first level of entries within our directory. If an entry is a subdirectory, we will not navigate into it.

We could build this logic ourselves, by implementing recursive behavior when fs::is_directory returns true. However, the standard library has built this for us, in the form of the fs::recursive_directory_iterator

Recursive Directory Iteration

The recursive directory iterator works the same way as the fs::directory_iterator, except it will navigate into any subdirectories it finds along the way.

The following code is almost identical to the previous example. Only a single line (which we’ve highlighted) has been changed, switching our iterator type:

#include <filesystem>
#include <iostream>
#include <ranges>
namespace fs = std::filesystem;

int main() {
  using std::ranges::subrange;

   subrange DirectoryEntries{
     fs::recursive_directory_iterator{
       R"(c:\test)"}};

  for (const auto& Entry : DirectoryEntries) {
    std::cout << Entry.path().string();

    if (Entry.is_directory()) {
      std::cout << " (Directory)\n";
    } else if (Entry.is_regular_file()) {
      std::cout << " (" << Entry.file_size()
        << " Bytes)\n";
    }
  }
}

We can now navigate into subdirectories:

c:\test\file.txt (0 Bytes)
c:\test\hello.backup (24 Bytes)
c:\test\hello.txt (24 Bytes)
c:\test\subdirectory (Directory)
c:\test\subdirectory\deep.txt (10 Bytes)

The recursive iterator has an additional method, called depth(). This will return the depth of the recursion, ie, how deeply nested the current directory entry is, relative to our starting point:

#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;

int main() {
  fs::recursive_directory_iterator Start{
      R"(c:\test)"};
  fs::recursive_directory_iterator End{};

  for (auto Iter{Start}; Iter != End; ++Iter) {
    std::cout << "Depth " << Iter.depth()
              << " - " << Iter->path().string()
              << '\n';
  }
}
Depth 0 - c:\test\file.txt
Depth 0 - c:\test\hello.backup
Depth 0 - c:\test\hello.txt
Depth 0 - c:\test\subdirectory
Depth 1 - c:\test\subdirectory\deep.txt

Up next, we’ll start working with file streams. These are the main ways we create, read, and update files within our file systems.

Summary

In this lesson, we've explored the essentials of directory iteration, demonstrating how to navigate and manage file systems using fs::directory_iterator and fs::recursive_directory_iterator.

Key Takeaways

  • Utilizing fs::directory_iterator: Learned how to utilize fs::directory_iterator to traverse a directory, handling each file and subdirectory within a given path using a straightforward for-loop.
  • Creating Subranges with Iterators: Gained skills in generating subranges from directory iterators, unlocking the ability to use range-based techniques and algorithms in our subdirectory.
  • Navigating Deeply with fs::recursive_directory_iterator: Explored the use of fs::recursive_directory_iterator for deep navigation into directories, automatically iterating through all subdirectories.
  • Employing the Depth Method: Acquired knowledge on using the depth() method of the recursive directory iterator to ascertain the level of recursion and understand the hierarchy of files and directories.

Was this lesson useful?

Next Lesson

File Streams

A detailed guide to reading and writing files in C++ using the standard library’s fstream type
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

Directory Iterators

An introduction to iterating through the file system, using directory iterators and recursive directory iterators

A computer programmer
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, Unlimited Access
Files and Serialization
A computer programmer
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 124 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

File Streams

A detailed guide to reading and writing files in C++ using the standard library’s fstream type
Abstract art representing computer programming
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved