Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Boost.filesystem to print stats about files and directories

In this tutorial we will see a command line tool to scan a directory and print stats about files, using boost::filesystem

Essentially, we are seeing a more high level version of the ls command you'd find in POSIX.

Reference for filesystem

To compile those programs, you would have to use this command:

g++ -o example example.cpp -lboost_filesystem

Reporting the size of one file

This simple program will print the size of a file in bytes. It has some limitations though: it will crash if a directory, or a file that doesn't exist is called. That is because file_size() only works with regular files.


#include <boost/filesystem.hpp>
#include <iostream>
using namespace boost::filesystem;

int main(int argc, char *argv[]) {
  if (argc < 2) {
    std::cout << "Usage: tut1 path\n";
    return 1;
  }
  std::cout << argv[1] << " " << file_size(argv[1]) << "\n";
  return 0;
}

Using status queries to determine file existence and type

boost::filesystem includes status query functions such as exists, is_directory and is_regular_file. These returns boolean values. Even though this new version works fine, we'd typically like to see the contents of a directory, not just that it exists.


#include <boost/filesystem.hpp>
#include <iostream>
using namespace boost::filesystem;

int main(int argc, char *argv[]) {
  if (argc < 2) {
    std::cout << "Usage: ./test path\n";
    return 1;
  }
  path p(argv[1]);
  if (exists(p)) {
    if (is_regular_file(p))
      std::cout << p << " size is " << file_size(p) << "\n";

    else if (is_directory(p))
      std::cout << p << " is a directory\n";

    else
      std::cout << " exists, but is not a regular file or directory\n";
  } else {
    std::cout << p << " does not exist\n";
  }

  return 0;
}

Directory iteration and catching exceptions

boost::filesystem has a directory_iterator class that is just what we need here. It follows the general pattern of the standard library's istream_iterator. Constructed from a path, it iterates over the contents of the directory. A default constructed directory_iterator acts as the end iterator.

The value type of directory_iterator is directory_entry. A directory_entry object contains path and file_status information. A directory_entry object can be used directly, but can also be passed to path arguments in function calls.

To increase robustness, we could have an error handling at each boost::filesystem function, but for simplicity a try/catch block is used here.


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

using std::cout;
using namespace boost::filesystem;

int main(int argc, char *argv[]) {
  if (argc < 2) {
    cout << "Usage: tut3 path\n";
    return 1;
  }

  path p(argv[1]);

  try {
    if (exists(p)) {
      if (is_regular_file(p))
        cout << p << " size is " << file_size(p) << "\n";
      else if (is_directory(p)) {
        cout << p << " is a directory containing:\n";
        for (directory_entry &x : directory_iterator(p))
          cout << "     " << x.path() << "\n";
      } else
        cout << p << " exists, but is not a regular file or directory\n";
    } else
      cout << p << " does not exist\n";
  } catch (const filesystem_error &ex) {
    cout << ex.what() << "\n";
  }

  return 0;
}

Some further improvements to consider:

  • Display only the filename, not the full path.
  • On Linux the listing isn't sorted. That's because we need to specify the ordering of directory iteration.

Path decomposition, and sorting results

For directories, we can build a std::vector of all the entries and then sort them before writing them to cout.


#include <algorithm>
#include <boost/filesystem.hpp>
#include <iostream>
#include <vector>
using std::cout;
using namespace boost::filesystem;

int main(int argc, char *argv[]) {
  if (argc < 2) {
    cout << "Usage: test path\n";
    return 1;
  }

  path p(argv[1]);

  try {
    if (exists(p)) {
      if (is_regular_file(p))
        cout << p << " size is " << file_size(p) << "\n";
      else if (is_directory(p)) {
        cout << p << " is a directory containing:\n";

        std::vector<path> v;

        for (auto &&x : directory_iterator(p))
          v.push_back(x.path());

        std::sort(v.begin(), v.end());

        for (auto &&x : v)
          cout << "    " << x.filename() << "\n";
      } else
        cout << p << " exists, but is not a regular file or a directory\n";
    } else
      cout << p << " does not exist\n";
  } catch (const filesystem_error &ex) {
    cout << ex.what() << "\n";
  }

  return 0;
}

filename() is one of several class path decomposition functions. It extracts the filename portion from a path (i.e. "index.html" from "/home/beman/boost/trunk/index.html").

You could replace this line:

v.push_back(x.path());

With:

v.push_back(x.path().filename());

Since we only care about the file name.