Harsha Phaneendra
Harsha Phaneendra

Reputation: 203

How to make std::filesystem::directory_iterator to list filenames in order

I am trying to get filenames in a directory "in order". I tried with c++ std::filesystem::directory_iterator to do so. As mentioned in https://en.cppreference.com/w/cpp/filesystem/directory_iterator it provides all filenames in the directory, excluding "." and ".." operator but not in order.

Kindly do help me out, Thanks in advance.

Here is the simple code i used

#include <iostream>
#include <string>
#include <experimental/filesystem>
#include <vector>

int main 
{
  // path to cereal folder 
  std::string path_to_cereal_folder = "/home/ros2/Documents";

  for (auto& entry : std::experimental::filesystem::directory_iterator(path_to_cereal_folder) )
  {
      std::cout << entry.path() << std::endl;     
  }
  return 0;
}

Upvotes: 20

Views: 26785

Answers (1)

Thomas Wilde
Thomas Wilde

Reputation: 901

As the reference states out, the iterator has no order. If you want to print the files in some order, you have to use other containers.


Printing in alphabetical order

Steps to take:

  1. Iterate over the files and insert the filenames into the set
  2. Iterate over the (sorted) set and print out the filenames. The entries in the set are sorted automatically.

I adapted your code and came to this solution:

#include <iostream>
#include <filesystem>
#include <set>

//--------------------------------------------------------------------------//
using namespace std;
namespace fs = std::filesystem;

//--------------------------------------------------------------------------//
int main() {
  string path_name = "/bin";

  //--- filenames are unique so we can use a set
  set<fs::path> sorted_by_name;

  for (auto &entry : fs::directory_iterator(path_name))
    sorted_by_name.insert(entry.path());

  //--- print the files sorted by filename
  for (auto &filename : sorted_by_name)
    cout << filename.c_str() << endl;
}

Printing sorted by timestamp

Steps to take:

  1. Iterate over the files and extract the timestamp
  2. Insert the files in a map, with its timestamp as sorting key
  3. Iterate over the (sorted) map and print out the filenames and the timestamp converted into something useful.

The helper function to convert the timestamp into a readable time was taken from here.

#include <iostream>
#include <filesystem>
#include <chrono>
#include <map>

//--------------------------------------------------------------------------//
using namespace std;

//--------------------------------------------------------------------------//
//--- helper function convert timepoint to usable timestamp
template <typename TP>
time_t to_time_t(TP tp) {
  using namespace chrono;
  auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now() + system_clock::now());
  return system_clock::to_time_t(sctp);
}

//--------------------------------------------------------------------------//
namespace fs = std::filesystem;
int main()
{
  string path_name = "/bin";
  map<time_t, fs::directory_entry> sort_by_time;

  //--- sort the files in the map by time
  for (auto &entry : fs::directory_iterator(path_name))
    if (entry.is_regular_file()) {
      auto time = to_time_t(entry.last_write_time());
      sort_by_time[time] = entry;
    }

  //--- print the files sorted by time
  for (auto const &[time, entry] : sort_by_time) {
    string timestamp = asctime(std::localtime(&time));
    timestamp.pop_back(); // remove automatic linebreak
    cout << timestamp << "\t " << entry.path().c_str() << endl;
  }
}

Printing sorted by filesize

Steps to take:

  1. Iterate over the files and extract the filesize
  2. Insert the files in a map, with its filesize as sorting key
  3. Iterate over the (sorted) map and print out the filenames and the filesize converted into something useful.

The helper function to convert the filesize into a readable information was taken from the cpp-reference.

#include <iostream>
#include <filesystem>
#include <map>
#include <cmath>

//--------------------------------------------------------------------------//
using namespace std;
namespace fs = std::filesystem;

//--------------------------------------------------------------------------//
//--- helper function convert the filesize into something meaningful
struct HumanReadable { uintmax_t size {}; }; 
template <typename Os> Os& operator<< (Os& os, HumanReadable hr) {
    int i{};
    double mantissa = hr.size;
    for (; mantissa >= 1024.; ++i) {
        mantissa /= 1024.;
    }
    mantissa = std::ceil(mantissa * 10.) / 10.;
    os << mantissa << "BKMGTPE"[i];
    return i == 0 ? os : os << "B (" << hr.size << ')';
}

//--------------------------------------------------------------------------//
int main() {
  string path_name = "/bin";
  map<uintmax_t, fs::directory_entry> sort_by_size;

  //--- sort the files in the map by size
  for (auto &entry : fs::directory_iterator(path_name))
    if (entry.is_regular_file()) {
      auto size = entry.file_size();
      sort_by_size[size] = entry;
    }

  //--- print the files sorted by size
  for (auto const &[size, entry] : sort_by_size)
    cout << HumanReadable{size} << "\t " << entry.path().c_str() << endl;  
}

Upvotes: 41

Related Questions