Recursive function for listing all files in sub directories

Unless your goal is to learn how to write a recursive function, you might prefer this simple loop based on Boost.Filesystem:

#include "boost/filesystem.hpp"
#include <iostream>

int main () {
  for ( boost::filesystem::recursive_directory_iterator end, dir("./");
    dir != end; ++dir ) {
    // std::cout << *dir << "\n";  // full path
    std::cout << dir->path().filename() << "\n"; // just last bit
  }
}

Or even the single function call:

std::copy(
  boost::filesystem::recursive_directory_iterator("./"),
  boost::filesystem::recursive_directory_iterator(),
  std::ostream_iterator<boost::filesystem::directory_entry>(std::cout, "\n"));

My approach in C++11:

#include <string>
#include <functional>
#include <dirent.h>

void listFiles(const std::string &path, std::function<void(const std::string &)> cb) {
    if (auto dir = opendir(path.c_str())) {
        while (auto f = readdir(dir)) {
            if (!f->d_name || f->d_name[0] == '.') continue;
            if (f->d_type == DT_DIR) 
                listFiles(path + f->d_name + "/", cb);

            if (f->d_type == DT_REG)
                cb(path + f->d_name);
        }
        closedir(dir);
    }
}

Usage:

listFiles("my_directory/", [](const std::string &path) {
    std::cout << path << std::endl;
});

Here is a version using proposed standard filesystem library:

#include <iostream>
#include <filesystem>

using namespace std;
using namespace std::tr2::sys;

void main()
{   
  for (recursive_directory_iterator i("."), end; i != end; ++i) 
    if (!is_directory(i->path()))
      cout << i->path().filename() << "\n";
} 

Isolate that code in a procedure that takes the base directory path as a parameter, so you can actually perform the recursive call. It should be something like

void recursive_file_list(const char * directory)
{
    // ...
}

Then, to check if the pdir you obtained is a directory, you have two routes:

  • you can check if pdir->d_type==DT_DIR; this gives you this information immediately, but it's not portable (POSIX does not mandate the existence of the d_type member); also, it's not supported for all the filesystems, so you may get DT_UNKNOWN. If you want to follow symlinks, you have to perform extra checks also if you get DT_LNK. In these cases, you must fall back to lstat (see the point below);
  • you can instead portably use lstat to get information about each file, checking in particular the st_mode field of struct stat.

Tags:

C++