0

I know how to get the list of files in Unix. The c++ program that I tried is below. Now how do I print the largest files in descending order?

int main() {
    DIR* drstrm = opendir(".");

    if (drstrm == NULL) {
        perror("error opening directory");
        return 1;
    }

    struct dirent* directoryentry = readdir(drstrm);

    while (directoryentry != NULL) {
        cout << (*directoryentry).d_name << endl;
        directoryentry = readdir(drstrm);
    }

    return 0;
}
pantherzx
  • 1
  • 3
  • 1
    Can you use the C++17 standard filesystem library? – Shawn Apr 05 '19 at 00:14
  • 1
    And `(*foo).bar` is normally written as `foo->bar`, btw. – Shawn Apr 05 '19 at 00:16
  • I didn't necessarily intend to close it as an exact duplicate by one link, but it did that automatically. :-/ In any case, [readdir() does not specify an order](https://stackoverflow.com/questions/8977441/does-readdir-guarantee-an-order). The direction I meant to point you at is just that this is basically "you need to put the information into structures and sort them". Hence your question is not about files, but a simpler one, like "how do I make/sort a list"--for which there are many answers to research from. – HostileFork says dont trust SE Apr 05 '19 at 00:18
  • I am just suppose to do this for the files for which IS_REG is true. Any idea how to do that for those specific files? – pantherzx Apr 05 '19 at 00:21
  • Yes you can @shawn. – pantherzx Apr 05 '19 at 00:27
  • Is this for `Linux` or any `POSIX` system? – Galik Apr 05 '19 at 00:27
  • Yes it is for Linux @Galik – pantherzx Apr 05 '19 at 00:36

2 Answers2

2

Since you said you can use C++17, the filesystem library it introduces makes this really easy (And portable to systems that don't have opendir()/readdir()):

#include <iostream>
#include <vector>
#include <filesystem>
#include <algorithm>
#include <string>

int main(int argc, char **argv) {      
  if (argc != 2) {
    std::cerr << "Usage: " << argv[0] << " DIRECTORY\n";
    return 1;
  }

  std::vector<std::filesystem::directory_entry> files;

  for (const auto &dirent : std::filesystem::directory_iterator(argv[1])) {
    if (dirent.is_regular_file()) {
      files.push_back(dirent);
    }
  }

  std::sort(files.begin(), files.end(), [](const auto &a, const auto &b){
      return a.file_size() > b.file_size(); });

  for (const auto &dirent : files) {
    // Quotes the filenames
    // std::cout << dirent.path() << '\n';
    // Doesn't quote
    std::cout << static_cast<std::string>(dirent.path()) << '\n';
  }

  return 0;
}

Usage:

$ g++-8 -std=c++17 -O -Wall -Wextra test.cpp -lstdc++fs
$ ./a.out .
a.out
bigfile.txt
test.cpp
smallfile.txt
etc.

If you can't use C++17, the same approach still holds: Put the file names and their sizes in a vector, and sort based on the sizes using > instead of the normal < (Which would sort from smallest to largest). On POSIX systems, you can get the file size with stat(2).

Shawn
  • 47,241
  • 3
  • 26
  • 60
0

To do this you are going to have to read the file info into a data structure (like a std::vector) and then sort the file info according to their size.

The old fashioned way could go something like this:

DIR* drstrm = opendir(".");

if(drstrm == NULL)
    throw std::runtime_error(std::strerror(errno));

struct stat st; // this is to use decltype

// keep info from dirent & stat in one place
struct file_info
{
    std::string name;
    decltype(st.st_size) size;
};

// store list of files here to be sorted
std::vector<file_info> files;

while(dirent* entry = readdir(drstrm))
{
    // get file info
    if(::stat(entry->d_name, &st) == -1)
        throw std::runtime_error(std::strerror(errno));

    // is it a regular file?
    if(!S_ISREG(st.st_mode))
        continue;

    // store it ready for sorting
    files.push_back({entry->d_name, st.st_size});
}

// sort the file_info objects according to size
std::sort(std::begin(files), std::end(files), [](file_info const& a, file_info const& b){
    return a.size < b.size;
});

// print them out
for(auto const& file: files)
    std::cout << file.name << ": " << file.size << '\n';

Fortunately in newer versions of C++ (C++17) you can use the new <filesystem> standard library:

namespace fs = std::filesystem; // for brevity

std::vector<fs::path> files;

for(auto const& ent: fs::directory_iterator("."))
{
    if(!fs::is_regular_file(ent))
        continue;

    files.push_back(ent);
}

std::sort(std::begin(files), std::end(files), [](fs::path const& a, fs::path const& b){
    return fs::file_size(a) < fs::file_size(b);
});

for(auto const& file: files)
    std::cout << file << ": " << fs::file_size(file) << '\n';
Galik
  • 47,303
  • 4
  • 80
  • 117