17

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;
}
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Harsha Phaneendra
  • 173
  • 1
  • 1
  • 5

1 Answers1

34

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;  
}
Thomas Wilde
  • 801
  • 7
  • 15
  • 2
    Thanks for a ready made solution, including all those gory details – Ichthyo Jul 24 '21 at 14:48
  • How to solve the 10 before the 2 (1, 10, 2, 3..) order problem? – Pedro77 Jun 03 '22 at 17:17
  • The strings are sorted alpha numericly. I think what you want is a lexicographic sorting. You have to provide your own class that sorts the strings. Have a look at https://stackoverflow.com/questions/2783814/c-string-sort-like-a-human-being. – Thomas Wilde Jun 04 '22 at 18:15
  • 2
    This is a very helpful answer. I think it could be made still better by using `std::multimap` instead of `std::map`, when sorting by file size or timestamp, since those attributes might not be unique per file. – Stephen G Tuggy Jun 26 '22 at 15:54
  • 2
    @Pedro77 I looked up the problem. I think the solution is out of scope for the original question. What you want is 'natural sort'. [Here](https://github.com/scopeInfinity/NaturalSort) you can find a GitHub repo with a function that provides natural sorting. You have to tell the `set` from the alphabetical order to use this comparator for sorting. – Thomas Wilde Jul 06 '22 at 11:00