10

I'm trying to write a function that returns a list of all files on current folder and all of its sub folders. I wrote this code:

#include <iostream>
#include <dirent.h>
#include <cstring>

using namespace std;

int main() {
   DIR* dir; dirent* pdir;
   //From my workspace
   dir=opendir(".");     
   while (pdir=readdir(dir)) {
       if(/**********This pdir is a directory**********/) {
           /**********RECURSIVE CALL SHOULD BE HERE**********/
           cout<<pdir->d_name<<endl;
       }
   }
   closedir(dir);
   return 0;
}

I searched for it in google and I don't know how to:

  • Check if the current pdir is directory
  • Go inside the directory and perform the recursive call on it

Meanwhile I have everything on main because I still don't know what arguments the recursive function should have.

Any hints?

Maroun
  • 94,125
  • 30
  • 188
  • 241
  • For what platform? *edit*: in what order? preorder, inorder, or postorder? – Wug Oct 29 '12 at 20:46
  • 1
    Have a look at http://stackoverflow.com/questions/3844546/how-do-i-check-if-a-directory-is-a-file-or-folder for your first question, and then http://msdn.microsoft.com/en-us/library/windows/desktop/aa363806(v=vs.85).aspx or the posix call "chdir(2)" for your second one – HRÓÐÓLFR Oct 29 '12 at 20:47
  • I don't care about the order as long as I can list all the files. – Maroun Oct 29 '12 at 20:48
  • Note that one may **not** recursively invoke `main()`. – Robᵩ Oct 29 '12 at 20:51
  • @Robᵩ indeed.. I edited the question. – Maroun Oct 29 '12 at 20:51
  • 2
    Have a look at this [boost filesystem](http://www.boost.org/doc/libs/1_51_0/libs/filesystem/doc/tutorial.html#Directory-iteration) tutorial. You need the library of course. – didierc Oct 29 '12 at 20:56

11 Answers11

13

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";
} 
Paul Jurczak
  • 7,008
  • 3
  • 47
  • 72
  • 1
    Notice that std::filesystem is now accepted in at least C++17 standard, so changing used namespace from `std::tr2::sys` to `std::filesystem` might suffice. – StormByte Sep 10 '22 at 01:08
9

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;
});
AdrianEddy
  • 707
  • 1
  • 8
  • 13
  • 4
    Another error on `listFiles(path + f->d_name + "/", cb);`: it should be `listFiles(path + "/" + f->d_name + "/", cb);` – markzzz Oct 17 '19 at 07:51
  • this is a good answer for systems which don't implement std::filesystem – Sergei Feb 04 '23 at 18:59
8

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"));
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • A minor issue with your example is that it lists directories in addition to files - see my answer to exclude directories. – Paul Jurczak May 14 '14 at 15:22
5

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.
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • Even if `d_type` exists you have to have the fallback to `stat` because the filesystem isn't required to fill in `d_type` even if the OS supports it. Also, `d_type` values are *not* bitmasks, you just want `(pdir->d_type == DT_DIR)`. – zwol Oct 29 '12 at 20:52
5

Using C++17 recursive_directory_iterator it becomes as concise as:

void ls_recursive(const std::filesystem::path& path) {
    for(const auto& p: std::filesystem::recursive_directory_iterator(path)) {
        if (!std::filesystem::is_directory(p)) {
            std::cout << p.path() << '\n';
        }
    }
}

With example output:

"/home/user/prj/rust/stack/Cargo.toml"
"/home/user/prj/rust/stack/.gitignore"
"/home/user/prj/rust/stack/src/main.rs"
"/home/user/prj/rust/stack/.git/config"
Leśny Rumcajs
  • 2,259
  • 2
  • 17
  • 33
2

It uses standard c++ functionality. No need to include any third party library in code.

Only send directory path as parameter. It will revert you every files path present in that folder and its sub folder.

Further that, if you need to sort any specific type file (i.e. .txt or .jpg), pass extension, it will print all the files path which having respective extension.

#include <Windows.h>
#include<iostream>
#include<vector>
#include<string>
using namespace std;

vector<string> files;

std::string Recursive(std::string folder) {
    std::string search_path = folder + "/*.*";
    WIN32_FIND_DATA fd;
    HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd);
    std::string tmp;
    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                if (!(!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, ".."))) {
                    tmp = folder + "\\";
                    tmp = tmp + fd.cFileName;
                    Recursive(tmp);
                }
            }
            else {
                std::string FinalFilePath = folder + "\\" + fd.cFileName;
                files.push_back(FinalFilePath);
            }

        } while (::FindNextFile(hFind, &fd));
        ::FindClose(hFind);
    }
    return folder;
}

bool has_suffix(const std::string& str, const std::string& suffix) {
    return str.size() >= suffix.size() &&
        str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}

int main(){
std::string folder = "C:\\Users\\Omkar\\Desktop\\Test";
    Recursive(folder);
    std::string t;
    const auto needle = std::string(".txt");
    while (!files.empty()) {
        t = files.back();
        if (has_suffix(t, ".mmt")) {
            cout << "FINAL PATH : " << t << endl;
            t.clear();
        }
        files.pop_back();
    }
return 0;
}
Omkar
  • 343
  • 3
  • 12
1

Path should look like /your_path/. For search inside hidden folders you should add third parameter true.

#include <dirent.h>
#include <vector>
#include <cstring>    

void GetReqDirs(const std::string& path, std::vector<string>& files,const bool showHiddenDirs = false){
    DIR *dpdf;
    struct dirent *epdf;
    dpdf = opendir(path.c_str());
    if (dpdf != NULL){
        while ((epdf = readdir(dpdf)) != NULL){
            if(showHiddenDirs ? (epdf->d_type==DT_DIR && string(epdf->d_name) != ".." && string(epdf->d_name) != "." ) : (epdf->d_type==DT_DIR && strstr(epdf->d_name,"..") == NULL && strstr(epdf->d_name,".") == NULL ) ){
                GetReqDirs(path+epdf->d_name+"/",files, showHiddenDirs);
            }
            if(epdf->d_type==DT_REG){
                files.push_back(path+epdf->d_name);
            }
        }
    }
    closedir(dpdf);
}
Victor
  • 2,321
  • 2
  • 13
  • 12
0

You could check if there is no "." in the string.

if(strstr(pdir->d_name,".") != NULL)
user1594121
  • 107
  • 1
  • 8
0

Here how I use it

std::vector<std::string> get_all_files_recursive(const fs::path& path)
    {
        std::vector<std::string> result;

        for (const auto& p : fs::recursive_directory_iterator(path))
        {
            if (!fs::is_directory(p))
            {
                fs::path path = p.path();
                result.push_back(path.u8string());
            }
        }

        return result;
    }
Sirop4ik
  • 4,543
  • 2
  • 54
  • 121
0

Great stuff! Here is my Windows friendly solution using std only since i had few bumps with the above solutions on Windows specifically, works in DLLs as well:

 #include <windows.h> // messagebox

 #define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING

 #include <filesystem> // C++17 standard header file name
 #include <experimental/filesystem> // Header file for pre-standard implementation

 using namespace std::experimental::filesystem::v1;

 for (recursive_directory_iterator next(path(dir.c_str())), end; next != end; ++next)
 {
    auto path = next->path();
    if (path.has_extension())
    {
        MessageBox(0, path.wstring().c_str(), L"Filepath", 0);
    }
 }
binarytrails
  • 516
  • 4
  • 14
0

A simple approach is c based. It's using dirent for list the files/folders and stat for get files/folders informations.

Then you recall function if resource is directory.

Note that you can't call recursively on . (current folder instance) and .. (parent folder instance)

This following code give you an idea. The advantage is ability to working on most versions of c++.

#include <iostream>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <dirent.h>

void printdir(std::string dirname)
{
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir (dirname.c_str())) != NULL)
    {
        /* print all the files and directories within directory and sub-directories */
        while ((ent = readdir (dir)) != NULL)
        {
            struct stat s;
            std::string path = dirname + "\\" + ent->d_name;
            std::cout << path << " ";
            if(stat(path.c_str(), &s) == 0)
            {
                if(s.st_mode & S_IFDIR)
                {
                    //it's a directory
                    std::cout << "(directory)" << std::endl;
                    //don't call recursively for . and ..
                    if(std::string(ent->d_name) != "." && std::string(ent->d_name) != "..")
                    {
                        printdir(path);
                    }
                }
                else if(s.st_mode & S_IFREG)
                {
                    //it's a file
                    std::cout << "(file)" << std::endl;
                }
                else
                {
                    std::cout << "(unknow)" << std::endl;
                    //something else
                }
            }
            else
            {
                //error
                std::cout << "(error)" << std::endl;
            }
        }
        closedir (dir);
    }
    else
    {
        /* could not open directory */
        perror ("");
    }
}

int main()
{
    std::string dirname(".");

    printdir(dirname);
    return 0;
} 
le Mandarin
  • 192
  • 10