788

How can I determine the list of files in a directory from inside my C or C++ code?

I'm not allowed to execute the ls command and parse the results from within my program.

samoz
  • 56,849
  • 55
  • 141
  • 195
  • 8
    This is a duplicate of [609236](http://stackoverflow.com/questions/609203/reading-file-names/609236#609236) – chrish Mar 04 '09 at 20:35
  • 1
    See also [`stat()` error 'no such file or directory' when file name is returned by `readdir()`](http://stackoverflow.com/questions/5125919/stat-error-no-such-file-or-directory-when-file-name-is-returned-by-readdir). – Jonathan Leffler Dec 25 '14 at 01:54
  • 4
    @chrish - Yea but this one has the classic "I'm not allowed to execute the 'ls'"! It's *exactly* how I'd feel 1st year of Computer Science. ;D <3 x – James Bedford Oct 22 '16 at 11:50
  • 7
    C and C++ are not the same language. Therefore, the procedure to accomplish this task will be different in both languages. Please chose one and re-tag accordingly. – MD XF Mar 02 '17 at 21:58
  • 4
    And neither of those languages (other than C++ since C++17) even has a concept of a directory - so any answer is likely to be dependent on your OS, or on any abstraction libraries you might be using. – Toby Speight Feb 20 '18 at 11:38
  • @chrish I thought so too but the OP of that question specifically mentioned Windows though they didn't include it in the question title or tags. Half the answers there are Win32 specific. – hippietrail Dec 21 '22 at 05:09
  • @hippietrail you know this question is 13 years old, right? – samoz Dec 22 '22 at 06:24
  • 1
    @samoz yep and it's stayed active the whole time. currently "highly active" apparently. – hippietrail Dec 22 '22 at 07:04

33 Answers33

1077

UPDATE 2017:

In C++17 there is now an official way to list files of your file system: std::filesystem. There is an excellent answer from Shreevardhan below with this source code:

#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main()
{
    std::string path = "/path/to/directory";
    for (const auto & entry : fs::directory_iterator(path))
        std::cout << entry.path() << std::endl;
}

Old Answer:

In small and simple tasks I do not use boost, I use dirent.h. It is available as a standard header in UNIX, and also available for Windows via a compatibility layer created by Toni Rönkkö.

DIR *dir;
struct dirent *ent;
if ((dir = opendir ("c:\\src\\")) != NULL) {
  /* print all the files and directories within directory */
  while ((ent = readdir (dir)) != NULL) {
    printf ("%s\n", ent->d_name);
  }
  closedir (dir);
} else {
  /* could not open directory */
  perror ("");
  return EXIT_FAILURE;
}

It is just a small header file and does most of the simple stuff you need without using a big template-based approach like boost (no offence, I like boost!).

Leo
  • 100
  • 10
Peter Parker
  • 29,093
  • 5
  • 52
  • 80
  • I liked tinydir at this answer: http://stackoverflow.com/a/14679177/901641 I prefer more straight forward downloads (don't give me a list of choices, give me the right choice. Honestly, C/C++ already made navigating directories hard enough already; why would I want to screw around with that?) – ArtOfWarfare May 18 '13 at 23:19
  • 8
    @ArtOfWarfare: tinydir was not even created when this question was answered. Also it is a wrapper around dirent (POSIX) and FindFirstFile (Windows) , while dirent.h just wraps dirent for windows. I think it is a personal taste, but dirent.h feels more as a standard – Peter Parker May 20 '13 at 19:13
  • 1
    @Peter Parker - I just wanted to prop up that answer since it hasn't received as much attention yet and of the few here that I tried it was my favorite. – ArtOfWarfare May 20 '13 at 23:01
  • 1
    Why doesn't this create a memory leak? I can understand closedir(dir) would clean *dir up, but what about *ent? – Josh C Sep 24 '14 at 14:04
  • 9
    @JoshC: because *ent is just a returned pointer of the internal representation. by closing the directory you will eliminate the *ent as well. As the *ent is only for reading, this is a sane design, i think. – Peter Parker Sep 25 '14 at 11:41
  • 1
    Please note that readdir does NOT guarantee the order of the files being read. Refer to http://stackoverflow.com/questions/8977441/does-readdir-guarantee-an-order – biubiuty Apr 29 '15 at 04:25
  • 1
    Not included in Windows by default, so this isn't very portable. http://stackoverflow.com/questions/5530933/dirent-h-in-visual-studio-2010-or-2008 – Joe Oct 09 '15 at 18:51
  • @JoshC same reason `fopen` doesn't create a memory leak when paired with `fclose`. – user253751 Nov 22 '15 at 08:15
  • 2
    doesn't exist on VS15. – shinzou Dec 05 '15 at 16:20
  • 1
    My two cents: I put this code into a C++ class [here](https://gist.github.com/tinchou/5d85a8b3716a788cbdd8) – Martín Coll Dec 14 '15 at 23:29
  • 4
    Don't waste your time - **there is no ``, `opendir` etc. in MSVC!** I don't understand why people upvote this answer – rustyx Apr 15 '16 at 08:48
  • 63
    people get real!! this is a question from 2009 and it has not even mentioned VS. So do not criticize that your full proprietary (although quite nice) IDE is not supporting centuries old OS standards. Also my answer said it is "available" for windows, not "included" in any IDE from now and for all times ... I am pretty sure you can download dirent and put it in some include dir and voila there it is. – Peter Parker Apr 15 '16 at 09:43
  • 2
    @rustyx Because it (a) provides a link to a compatibility layer for Windows (b) never once said that the solution worked out-of-the-box on all machines everywhere (c) works – Nic Jul 03 '16 at 07:07
  • 8
    The answer is misleading. It should begin with: "*...I use **dirent.h**, for which a Windows open-source compatibility layer [also exists](https://github.com/tronkko/dirent)*". – rustyx Jul 03 '16 at 19:43
  • it works, but if I display the names of files found in directory, I get "." and ".." in the list of files. why so? can I edit code to exclude these options? I need to iterate over list of files and this could be a problem – ubisum Sep 24 '16 at 15:11
  • should have mentioned that it requires `#include `, `#include ` and `#include `. – FluorescentGreen5 Oct 13 '16 at 07:24
  • I'm trying to include dirent.h but I got error - `can't open source file` – STF Mar 27 '17 at 04:30
  • 17
    With C++14 there is `std::experimental::filesystem`, with C++17 there is `std::filesystem`. See answer of Shreevardhan below. So no need for 3rd party libraries. – Roi Danton Apr 13 '17 at 07:49
  • 1
    it doesn't support the file path which is encoded with GBK. – Allen Hong Aug 02 '17 at 10:22
  • I think using the c++17 solution requires a std::cout << p.path().string() instead of std::cout << p?! – Wolfgang Aug 23 '18 at 09:30
  • How can I exclude "." and ".." (current and previous directory) from the result of method 1? – Kamran Hosseini Nov 16 '18 at 17:12
  • 1
    just compare the strings..? – Peter Parker Nov 20 '18 at 12:54
504

C++17 now has a std::filesystem::directory_iterator, which can be used as

#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
 
int main() {
  std::string path = "/path/to/directory";

  for (const auto & entry : fs::directory_iterator(path)) {
    std::cout << entry.path() << std::endl;
  }
}

Also, std::filesystem::recursive_directory_iterator can iterate the subdirectories as well.

c0g
  • 2,665
  • 1
  • 13
  • 5
Shreevardhan
  • 12,233
  • 3
  • 36
  • 50
  • 41
    AFAIK can also be used in C++14, but there it is still experimental: `namespace fs = std::experimental::filesystem;` . It seems to work ok though. – PeterK Jul 11 '16 at 08:03
  • 10
    This should be the preferred answer for current use (starting with C++17) – green diod Jan 03 '17 at 18:28
  • 1
    Just be aware that current implementation of recursive_directory_iterator and _Directory_iterator skip through directories that it cannot open due to permission issues – db_ Jan 03 '17 at 18:50
  • 5
    Heed when passing `std::filesystem::path` to `std::cout`, the quotation marks are included in the output. To avoid that, append `.string()` to the path to do an explicit instead of an implicit conversion (here `std::cout << p.string() << std::endl;`). Example: http://coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3 – Roi Danton Apr 13 '17 at 09:04
  • 2
    What about NON-ASCII characters in file names? Shouldn't `std::wstring` be used or what's the type from the iterator? – Snackoverflow Jan 19 '18 at 13:46
  • 1
    How do you place the file path in "p" into a string (or wstring)? All examples I've found cases errors when compiling with Visual Studio 2017. For example "error C2039: 'string': is not a member of 'std::experimental::filesystem::v1::directory_entry'" for "p.string()" and "error C3867: 'std::experimental::filesystem::v1::directory_entry::path': non-standard syntax; use '&' to create a pointer to member" for "wstring path = p.path;". – Björn Larsson Feb 19 '18 at 13:57
  • 2
    @BjörnLarsson The example in my comment above is incomplete since the `p` inside the answer is the [`directory_iterator`](https://en.cppreference.com/w/cpp/filesystem/directory_iterator) instead of the path (got confused by that since the term `p` is usually used for `std::filesystem::path`, sorry). `p.path().string()` for narrow strings respectively `p.path().wstring()` for wide strings should work. – Roi Danton Jul 19 '18 at 15:50
  • To correct myself again, `p` in the answer is the [`directory_entry`](https://en.cppreference.com/w/cpp/filesystem/directory_entry). – Roi Danton Jul 19 '18 at 16:01
  • 5
    I'm not sure if I'm alone in this, but without linking to `-lstdc++fs`, I'd get a `SIGSEGV (Address boundary error)`. I couldn't find anywhere in the documentation that this was required, and linker didn't give any clue either. This worked for both `g++ 8.3.0` and `clang 8.0.0-3`. Does anyone have any insight is to where things like this is specified in the docs/specs? – swalog Jul 02 '19 at 10:16
  • 1
    For C++14 please change the header to `#include ` – liorko Jul 08 '19 at 13:15
  • 1
    Actually, without the flag "-lstdc++fs" I couldn't even compile (`collect2: error: ld returned 1 exit status`). I bet it is required because I'm using `` under c++14, which provides it as an experimental feature. – ljleb May 07 '20 at 01:17
  • Do not use `std::endl`. It unconditionally flushes the stream. Use `"\n"` instead. – Johannes Overmann Dec 11 '21 at 11:32
  • It prints out files, not only directories. – Pedro77 May 24 '22 at 20:45
  • How do you do make it print the output/files sorted? – Gabriel Staples Jun 23 '22 at 06:04
  • Found it! Simply store all paths into a `std::set<>` to automatically sort them alphabetically, then print the set. See: [How to make std::filesystem::directory_iterator to list filenames in order](https://stackoverflow.com/a/62412605/4561887). I ran some of my own tests to do this here: [std_experimental_filesystem__list_files_and_dirs.cpp](https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/cpp/std_experimental_filesystem__list_files_and_dirs.cpp). – Gabriel Staples Jun 23 '22 at 06:27
239

Unfortunately the C++ standard does not define a standard way of working with files and folders in this way.

Since there is no cross platform way, the best cross platform way is to use a library such as the boost filesystem module.

Cross platform boost method:

The following function, given a directory path and a file name, recursively searches the directory and its sub-directories for the file name, returning a bool, and if successful, the path to the file that was found.

bool find_file(const path & dir_path,         // in this directory,
               const std::string & file_name, // search for this name,
               path & path_found)             // placing path here if found
{
    if (!exists(dir_path)) 
        return false;

    directory_iterator end_itr; // default construction yields past-the-end

    for (directory_iterator itr(dir_path); itr != end_itr; ++itr)
    {
        if (is_directory(itr->status()))
        {
            if (find_file(itr->path(), file_name, path_found)) 
                return true;
        }
        else if (itr->leaf() == file_name) // see below
        {
            path_found = itr->path();
            return true;
        }
    }
    return false;
}

Source from the boost page mentioned above.

For Unix/Linux based systems:

You can use opendir / readdir / closedir.

Sample code which searches a directory for entry ``name'' is:

len = strlen(name);
dirp = opendir(".");
while ((dp = readdir(dirp)) != NULL)
        if (dp->d_namlen == len && !strcmp(dp->d_name, name)) {
                (void)closedir(dirp);
                return FOUND;
        }
(void)closedir(dirp);
return NOT_FOUND;

Source code from the above man pages.

For a windows based systems:

You can use the Win32 API FindFirstFile / FindNextFile / FindClose functions.

The following C++ example shows you a minimal use of FindFirstFile.

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

void _tmain(int argc, TCHAR *argv[])
{
   WIN32_FIND_DATA FindFileData;
   HANDLE hFind;

   if( argc != 2 )
   {
      _tprintf(TEXT("Usage: %s [target_file]\n"), argv[0]);
      return;
   }

   _tprintf (TEXT("Target file is %s\n"), argv[1]);
   hFind = FindFirstFile(argv[1], &FindFileData);
   if (hFind == INVALID_HANDLE_VALUE) 
   {
      printf ("FindFirstFile failed (%d)\n", GetLastError());
      return;
   } 
   else 
   {
      _tprintf (TEXT("The first file found is %s\n"), 
                FindFileData.cFileName);
      FindClose(hFind);
   }
}

Source code from the above msdn pages.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Brian R. Bondy
  • 339,232
  • 124
  • 596
  • 636
106

One function is enough, you don't need to use any 3rd-party library (for Windows).

#include <Windows.h>

vector<string> get_all_files_names_within_folder(string folder)
{
    vector<string> names;
    string search_path = folder + "/*.*";
    WIN32_FIND_DATA fd; 
    HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd); 
    if(hFind != INVALID_HANDLE_VALUE) { 
        do { 
            // read all (real) files in current folder
            // , delete '!' read other 2 default folder . and ..
            if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
                names.push_back(fd.cFileName);
            }
        }while(::FindNextFile(hFind, &fd)); 
        ::FindClose(hFind); 
    } 
    return names;
}

PS: as mentioned by @Sebastian, you could change *.* to *.ext in order to get only the EXT-files (i.e. of a specific type) in that directory.

herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
  • 27
    This solution if platform-specific. That is the reason you need 3rd-party libraries. – kraxor May 29 '14 at 13:14
  • 12
    @kraxor Yes, it only works in Windows, but OP never asks to have a cross-platform solution. BTW, I always prefer to choose something without using 3rd-libraries (if possible). – herohuyongtao May 29 '14 at 14:13
  • 10
    @herohuyongtao OP never specified a platform, and giving a heavily platform-dependent solution to a generic question can be misleading. (What if there is a one-line solution that works only on PlayStation 3? Is that a good answer here?) I see you edited your answer to state that it only works on Windows, I guess it's fine this way. – kraxor May 29 '14 at 17:20
  • @kraxor I think it should be a good answer if you can write it one line for a specific platform, but using the language OP indicates, i.e. C/C++. – herohuyongtao May 29 '14 at 17:23
  • @herohuyongtao Well, I think a good answer is "there is no 'C/C++' solution, but here is how to do it on ". Or something along those lines. Anyway, that's just me. – kraxor May 29 '14 at 17:26
  • 3
    @herohuyongtao OP mentioned he can't parse ls, meaning he is probably on unix.. anyway, good answer for Windows. – Thomas Sep 18 '14 at 21:20
  • 1
    You should mention that the folder must end with a \ and if you only want some special files you could change *.* to e.g. *.dll and you get only the DLL-Files in that directory. Thats realy great but only on Windows ;-). – Sebastian Aug 19 '15 at 12:30
  • 1
    @Sebastian Thanks for the note and refined the answer accordingly. – herohuyongtao Aug 19 '15 at 14:48
  • 1
    The top accepted answer with is also platform specific to *nix. I'm on windows, so this answer is more relevant to me, so +1. :-) – Joe Oct 09 '15 at 18:53
  • Why `sprintf` and not a `c++` function? Also, why not a deque instead of a vector for O(1) insertion? – shinzou Dec 05 '15 at 16:30
  • Also, how can you send it the current directory of the project? – shinzou Dec 05 '15 at 16:32
  • 3
    I ended up using a `std::vector` and then `fileName.c_str()` instead of a vector of strings, which wouldn't compile. – PerryC Jul 12 '16 at 15:38
  • Is there any way I can get multiple extensions? For an example *.exe and *.dll in the same path. – hshantanu Apr 16 '17 at 21:13
  • cool ! I only change [Character Set] of Visual Studio Project setting to "Not Set" , and it works well. – bruce Jan 22 '18 at 03:03
  • 1
    The wildcard pattern `*.*` will retrieve all files with at least one dot in their filename. In Windows, the correct pattern to retrieve all files is simply `*`. MS-DOS required `*.*` to retrieve all files and it seems like the habit just stuck. Since most filenames tend to have a dot in them, this just fails silently on occasion and no-one notices. – Ferruccio Mar 29 '18 at 20:39
  • In which order did this function return the filenames.? If no order is there any way to sort the names based on date as in Windows Explorer sort by date? – krishnakumarcn Sep 06 '18 at 13:24
61

For a C only solution, please check this out. It only requires an extra header:

https://github.com/cxong/tinydir

tinydir_dir dir;
tinydir_open(&dir, "/path/to/dir");

while (dir.has_next)
{
    tinydir_file file;
    tinydir_readfile(&dir, &file);

    printf("%s", file.name);
    if (file.is_dir)
    {
        printf("/");
    }
    printf("\n");

    tinydir_next(&dir);
}

tinydir_close(&dir);

Some advantages over other options:

  • It's portable - wraps POSIX dirent and Windows FindFirstFile
  • It uses readdir_r where available, which means it's (usually) threadsafe
  • Supports Windows UTF-16 via the same UNICODE macros
  • It is C90 so even very ancient compilers can use it
congusbongus
  • 13,359
  • 7
  • 71
  • 99
  • 2
    Very nice suggestion. I haven't tested it on a windows computer yet but it works brilliantly on OS X. – ArtOfWarfare May 18 '13 at 23:15
  • The library doesn't support std::string, so you can't pass file.c_str() to the tinydir_open. It givers error C2664 during compilation on msvc 2015 in this case. – Stepan Yakovenko Oct 18 '17 at 09:20
  • @StepanYakovenko the author stated clearly that "For a C only solution" –  Sep 03 '21 at 11:12
36

I recommend using glob with this reusable wrapper. It generates a vector<string> corresponding to file paths that fit the glob pattern:

#include <glob.h>
#include <vector>
using std::vector;

vector<string> globVector(const string& pattern){
    glob_t glob_result;
    glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
    vector<string> files;
    for(unsigned int i=0;i<glob_result.gl_pathc;++i){
        files.push_back(string(glob_result.gl_pathv[i]));
    }
    globfree(&glob_result);
    return files;
}

Which can then be called with a normal system wildcard pattern such as:

vector<string> files = globVector("./*");
Chris Redford
  • 16,982
  • 21
  • 89
  • 109
  • 2
    Test that glob() returns zero. – Camille Goudeseune May 21 '15 at 16:51
  • I would like to use glob.h as you recommended. But still, I can't include the .h file : It says `No such file or directory`. Can you tell me how to solve this issue please ? – Tofuw Feb 23 '16 at 10:36
  • Note that this routine goes only one level deep (no recursion). It also doesn't do a quick check to determine whether it's a file or directory, which you can do easily by switching `GLOB_TILDE` with `GLOB_TILDE | GLOB_MARK` and then checking for paths ending in a slash. You'll have to make either modification to it if you need that. – Volomike May 15 '16 at 17:16
  • Is this cross-platform compatible? – Nikhil Augustine Jul 01 '18 at 07:11
  • Unfortunately you cannot find uniformly hidden files via `glob`. – Alex Aparin Oct 05 '18 at 11:30
32

I think, below snippet can be used to list all the files.

#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>

int main(int argc, char** argv) { 
    list_dir("myFolderName");
    return EXIT_SUCCESS;
}  

static void list_dir(const char *path) {
    struct dirent *entry;
    DIR *dir = opendir(path);
    if (dir == NULL) {
        return;
    }

    while ((entry = readdir(dir)) != NULL) {
        printf("%s\n",entry->d_name);
    }

    closedir(dir);
}

This is the structure used (present in dirent.h):

struct dirent {
    ino_t d_ino; /* inode number */
    off_t d_off; /* offset to the next dirent */
    unsigned short d_reclen; /* length of this record */
    unsigned char d_type; /* type of file */
    char d_name[256]; /* filename */
};
Nav
  • 19,885
  • 27
  • 92
  • 135
Shrikant
  • 744
  • 8
  • 18
26

Here is a very simple code in C++11 using boost::filesystem library to get file names in a directory (excluding folder names):

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

int main()
{
    path p("D:/AnyFolder");
    for (auto i = directory_iterator(p); i != directory_iterator(); i++)
    {
        if (!is_directory(i->path())) //we eliminate directories
        {
            cout << i->path().filename().string() << endl;
        }
        else
            continue;
    }
}

Output is like:

file1.txt
file2.dat
Bad
  • 4,967
  • 4
  • 34
  • 50
  • Hi, and where can I get this library? – Alexander Leon VI Jul 08 '15 at 19:30
  • 2
    @Alexander De Leon: You can get this library at their site http://www.boost.org/, read getting started guide first, then use their `boost::filesystem` library http://www.boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm – Bad Jul 08 '15 at 20:02
  • @Bad how would I change this to output the complete directory for each file. like I want D:/AnyFolder/file1.txt and so on? – Areeb Muzaffar Apr 19 '21 at 22:40
25

Why not use glob()?

#include <glob.h>

glob_t glob_result;
glob("/your_directory/*",GLOB_TILDE,NULL,&glob_result);
for(unsigned int i=0; i<glob_result.gl_pathc; ++i){
  cout << glob_result.gl_pathv[i] << endl;
}
Meekohi
  • 10,390
  • 6
  • 49
  • 58
11

Try boost for x-platform method

http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm

or just use your OS specific file stuff.

Tim
  • 20,184
  • 24
  • 117
  • 214
  • 3
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/20149263) – ice1000 Jun 28 '18 at 12:26
  • @ice1000 Seriously? This Q&A is from 2009 – Tim Jun 28 '18 at 13:30
9

Check out this class which uses the win32 api. Just construct an instance by providing the foldername from which you want the listing then call the getNextFile method to get the next filename from the directory. I think it needs windows.h and stdio.h.

class FileGetter{
    WIN32_FIND_DATAA found; 
    HANDLE hfind;
    char folderstar[255];       
    int chk;

public:
    FileGetter(char* folder){       
        sprintf(folderstar,"%s\\*.*",folder);
        hfind = FindFirstFileA(folderstar,&found);
        //skip .
        FindNextFileA(hfind,&found);        
    }

    int getNextFile(char* fname){
        //skips .. when called for the first time
        chk=FindNextFileA(hfind,&found);
        if (chk)
            strcpy(fname, found.cFileName);     
        return chk;
    }

};
mbinette
  • 5,094
  • 3
  • 24
  • 32
robertvarga
  • 91
  • 1
  • 1
6

GNU Manual FTW

http://www.gnu.org/software/libc/manual/html_node/Simple-Directory-Lister.html#Simple-Directory-Lister

Also, sometimes it's good to go right to the source (pun intended). You can learn a lot by looking at the innards of some of the most common commands in Linux. I've set up a simple mirror of GNU's coreutils on github (for reading).

https://github.com/homer6/gnu_coreutils/blob/master/src/ls.c

Maybe this doesn't address Windows, but a number of cases of using Unix variants can be had by using these methods.

Hope that helps...

Homer6
  • 15,034
  • 11
  • 61
  • 81
6

Shreevardhan answer works great. But if you want to use it in c++14 just make a change namespace fs = experimental::filesystem;

i.e.,

#include <string>
#include <iostream>
#include <filesystem>

using namespace std;
namespace fs = experimental::filesystem;

int main()
{
    string path = "C:\\splits\\";
    for (auto & p : fs::directory_iterator(path))
        cout << p << endl;
    int n;
    cin >> n;
}
Venkat Vinay
  • 89
  • 2
  • 5
6
#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main() {
    std::string path = "/path/to/directory";
    for (const auto & entry : fs::directory_iterator(path))
        std::cout << entry.path() << std::endl;
}
3ashry
  • 63
  • 1
  • 5
4

I hope this code help you.

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

string wchar_t2string(const wchar_t *wchar)
{
    string str = "";
    int index = 0;
    while(wchar[index] != 0)
    {
        str += (char)wchar[index];
        ++index;
    }
    return str;
}

wchar_t *string2wchar_t(const string &str)
{
    wchar_t wchar[260];
    int index = 0;
    while(index < str.size())
    {
        wchar[index] = (wchar_t)str[index];
        ++index;
    }
    wchar[index] = 0;
    return wchar;
}

vector<string> listFilesInDirectory(string directoryName)
{
    WIN32_FIND_DATA FindFileData;
    wchar_t * FileName = string2wchar_t(directoryName);
    HANDLE hFind = FindFirstFile(FileName, &FindFileData);

    vector<string> listFileNames;
    listFileNames.push_back(wchar_t2string(FindFileData.cFileName));

    while (FindNextFile(hFind, &FindFileData))
        listFileNames.push_back(wchar_t2string(FindFileData.cFileName));

    return listFileNames;
}

void main()
{
    vector<string> listFiles;
    listFiles = listFilesInDirectory("C:\\*.txt");
    for each (string str in listFiles)
        cout << str << endl;
}
Yas
  • 4,957
  • 2
  • 41
  • 24
  • 4
    -1. `string2wchar_t` returns the address of a local variable. Also, you should probably use the conversion methods available in WinAPI instead of writing your own ones. – Daniel Kamil Kozar Oct 23 '13 at 07:30
4
char **getKeys(char *data_dir, char* tablename, int *num_keys)
{
    char** arr = malloc(MAX_RECORDS_PER_TABLE*sizeof(char*));
int i = 0;
for (;i < MAX_RECORDS_PER_TABLE; i++)
    arr[i] = malloc( (MAX_KEY_LEN+1) * sizeof(char) );  


char *buf = (char *)malloc( (MAX_KEY_LEN+1)*sizeof(char) );
snprintf(buf, MAX_KEY_LEN+1, "%s/%s", data_dir, tablename);

DIR* tableDir = opendir(buf);
struct dirent* getInfo;

readdir(tableDir); // ignore '.'
readdir(tableDir); // ignore '..'

i = 0;
while(1)
{


    getInfo = readdir(tableDir);
    if (getInfo == 0)
        break;
    strcpy(arr[i++], getInfo->d_name);
}
*(num_keys) = i;
return arr;
}
3

This implementation realizes your purpose, dynamically filling an array of strings with the content of the specified directory.

int exploreDirectory(const char *dirpath, char ***list, int *numItems) {
    struct dirent **direntList;
    int i;
    errno = 0;

    if ((*numItems = scandir(dirpath, &direntList, NULL, alphasort)) == -1)
        return errno;

    if (!((*list) = malloc(sizeof(char *) * (*numItems)))) {
        fprintf(stderr, "Error in list allocation for file list: dirpath=%s.\n", dirpath);
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < *numItems; i++) {
        (*list)[i] = stringDuplication(direntList[i]->d_name);
    }

    for (i = 0; i < *numItems; i++) {
        free(direntList[i]);
    }

    free(direntList);

    return 0;
}
  • How would I call this? I'm getting segfaults when I try to run this function on the first `if` block. I'm calling it with `char **list; int numItems; exploreDirectory("/folder",list, numItems);` – Hal T Feb 21 '17 at 15:17
2

This works for me. I'm sorry if I cannot remember the source. It is probably from a man page.

#include <ftw.h>

int AnalizeDirectoryElement (const char *fpath, 
                            const struct stat *sb,
                            int tflag, 
                            struct FTW *ftwbuf) {

  if (tflag == FTW_F) {
    std::string strFileName(fpath);

    DoSomethingWith(strFileName);
  }
  return 0; 
}

void WalkDirectoryTree (const char * pchFileName) {

  int nFlags = 0;

  if (nftw(pchFileName, AnalizeDirectoryElement, 20, nFlags) == -1) {
    perror("nftw");
  }
}

int main() {
  WalkDirectoryTree("some_dir/");
}
ENHering
  • 55
  • 6
2

you can get all direct of files in your root directory by using std::experimental:: filesystem::directory_iterator(). Then, read the name of these pathfiles.

#include <iostream>
#include <filesystem>
#include <string>
#include <direct.h>
using namespace std;
namespace fs = std::experimental::filesystem;
void ShowListFile(string path)
{
for(auto &p: fs::directory_iterator(path))  /*get directory */
     cout<<p.path().filename()<<endl;   // get file name
}

int main() {

ShowListFile("C:/Users/dell/Pictures/Camera Roll/");
getchar();
return 0;
}
ducPham
  • 31
  • 1
  • 3
2

This answer should work for Windows users that have had trouble getting this working with Visual Studio with any of the other answers.

  1. Download the dirent.h file from the github page. But is better to just use the Raw dirent.h file and follow my steps below (it is how I got it to work).

    Github page for dirent.h for Windows: Github page for dirent.h

    Raw Dirent File: Raw dirent.h File

  2. Go to your project and Add a new Item (Ctrl+Shift+A). Add a header file (.h) and name it dirent.h.

  3. Paste the Raw dirent.h File code into your header.

  4. Include "dirent.h" in your code.

  5. Put the below void filefinder() method in your code and call it from your main function or edit the function how you want to use it.

    #include <stdio.h>
    #include <string.h>
    #include "dirent.h"
    
    string path = "C:/folder"; //Put a valid path here for folder
    
    void filefinder()
    {
        DIR *directory = opendir(path.c_str());
        struct dirent *direntStruct;
    
        if (directory != NULL) {
            while (direntStruct = readdir(directory)) {
                printf("File Name: %s\n", direntStruct->d_name); //If you are using <stdio.h>
                //std::cout << direntStruct->d_name << std::endl; //If you are using <iostream>
            }
        }
        closedir(directory);
    }
    
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
ZKR
  • 69
  • 4
2

I tried to follow the example given in both answers and it might be worth noting that it appears as though std::filesystem::directory_entry has been changed to not have an overload of the << operator. Instead of std::cout << p << std::endl; I had to use the following to be able to compile and get it working:

#include <iostream>
#include <filesystem>
#include <string>
namespace fs = std::filesystem;

int main() {
    std::string path = "/path/to/directory";
    for(const auto& p : fs::directory_iterator(path))
        std::cout << p.path() << std::endl;
}

trying to pass p on its own to std::cout << resulted in a missing overload error.

Michael Jasper
  • 7,962
  • 4
  • 40
  • 60
OpticalMagician
  • 133
  • 1
  • 9
2

Shreevardhan's design also works great for traversing subdirectories:

#include <string>
#include <iostream>
#include <filesystem>

using namespace std;
namespace fs = filesystem;
int main()
{
    string path = "\\path\\to\\directory";
    // string path = "/path/to/directory";
    for (auto & p : fs::recursive_directory_iterator(path))
        cout << p.path() << endl;
}

Compilation: cl /EHsc /W4 /WX /std:c++17 ListFiles.cpp

jarikmesik
  • 19
  • 4
2

Peter Parker's solution, but without using for:

#include <algorithm>
#include <filesystem>
#include <ranges>
#include <vector>

using namespace std;

int main() {
    vector<filesystem::path> filePaths;
    ranges::transform(filesystem::directory_iterator("."),     
    back_inserter(filePaths), [](const auto& dirFile){return dirFile.path();} );
}
Master Yoda
  • 176
  • 1
  • 8
1

System call it!

system( "dir /b /s /a-d * > file_names.txt" );

Then just read the file.

EDIT: This answer should be considered a hack, but it really does work (albeit in a platform specific way) if you don't have access to more elegant solutions.

Catalyst
  • 3,143
  • 16
  • 20
  • 7
    I'm not allowed to execute the 'ls' command and parse the results from within my program. I knew there would be someone that would send something like this... – yyny Apr 26 '15 at 15:37
  • For Windows, this is by far the most pragmatic way. Pay special attention to the `/A` switch. Whichever is the way you choose, security can seriously get-in-a-way here. If one is not "coding it in" from the start. Windows impersonations, authentications and other "deserts" are never easy to get right. – Chef Gladiator Dec 02 '20 at 11:36
1

Since files and sub directories of a directory are generally stored in a tree structure, an intuitive way is to use DFS algorithm to recursively traverse each of them. Here is an example in windows operating system by using basic file functions in io.h. You can replace these functions in other platform. What I want to express is that the basic idea of DFS perfectly meets this problem.

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

void TraverseFilesUsingDFS(const string& folder_path){
   _finddata_t file_info;
   string any_file_pattern = folder_path + "\\*";
   intptr_t handle = _findfirst(any_file_pattern.c_str(),&file_info);
   //If folder_path exsist, using any_file_pattern will find at least two files "." and "..", 
   //of which "." means current dir and ".." means parent dir
   if (handle == -1){
       cerr << "folder path not exist: " << folder_path << endl;
       exit(-1);
   }
   //iteratively check each file or sub_directory in current folder
   do{
       string file_name=file_info.name; //from char array to string
       //check whtether it is a sub direcotry or a file
       if (file_info.attrib & _A_SUBDIR){
            if (file_name != "." && file_name != ".."){
               string sub_folder_path = folder_path + "\\" + file_name;                
               TraverseFilesUsingDFS(sub_folder_path);
               cout << "a sub_folder path: " << sub_folder_path << endl;
            }
       }
       else
            cout << "file name: " << file_name << endl;
    } while (_findnext(handle, &file_info) == 0);
    //
    _findclose(handle);
}
tkingcer
  • 21
  • 4
1

Building on what herohuyongtao posted and a few other posts:

http://www.cplusplus.com/forum/general/39766/

What is the expected input type of FindFirstFile?

How to convert wstring into string?

This is a Windows solution.

Since I wanted to pass in std::string and return a vector of strings I had to make a couple conversions.

#include <string>
#include <Windows.h>
#include <vector>
#include <locale>
#include <codecvt>

std::vector<std::string> listFilesInDir(std::string path)
{
    std::vector<std::string> names;
    //Convert string to wstring
    std::wstring search_path = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(path);
    WIN32_FIND_DATA fd;
    HANDLE hFind = FindFirstFile(search_path.c_str(), &fd);
    if (hFind != INVALID_HANDLE_VALUE) 
    {
        do 
        {
            // read all (real) files in current folder
            // , delete '!' read other 2 default folder . and ..
            if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 
            {
                //convert from wide char to narrow char array
                char ch[260];
                char DefChar = ' ';
                WideCharToMultiByte(CP_ACP, 0, fd.cFileName, -1, ch, 260, &DefChar, NULL);
                names.push_back(ch);
            }
        } 
        while (::FindNextFile(hFind, &fd));
        ::FindClose(hFind);
    }
    return names;
}
tzg
  • 616
  • 1
  • 8
  • 17
  • 1
    If you know that you will be only using multibyte you could use `WIN32_FIND_DATAA`, `FindFirstFileA` and `FindNextFileA`. Then There will be no need to convert result to multibyte or Input to unicode. – Kiran Thilak Dec 06 '19 at 06:49
  • Just advice: `std::wstring_convert` is deprecated (a few years ago now). if you are using OS in some variety of English, perhaps [this might be a good enough replacement](https://dbj.org/c17-codecvt-deprecated-panic/), .. other than that vector of strings, and I assume with c++ exceptions in use, is the sure way to the largest and slowest solution. unless you use some of the few very good, std lib replacements ... – Chef Gladiator Dec 02 '20 at 11:49
1

Based on the answers above

#include <vector>
#include <string>
#include <algorithm>

#ifdef _WIN32
#include <windows.h>
std::vector<std::string> files_in_directory(std::string path)
{
    std::vector<std::string> files;

    // check directory exists
    char fullpath[MAX_PATH];
    GetFullPathName(path.c_str(), MAX_PATH, fullpath, 0);
    std::string fp(fullpath);
    if (GetFileAttributes(fp.c_str()) != FILE_ATTRIBUTE_DIRECTORY)
        return files;

    // get file names
    WIN32_FIND_DATA findfiledata;
    HANDLE hFind = FindFirstFile((LPCSTR)(fp + "\\*").c_str(), &findfiledata);
    if (hFind != INVALID_HANDLE_VALUE)
    {
        do 
        {
            files.push_back(findfiledata.cFileName);
        } 
        while (FindNextFile(hFind, &findfiledata));
        FindClose(hFind);
    }

    // delete current and parent directories
    files.erase(std::find(files.begin(), files.end(), "."));
    files.erase(std::find(files.begin(), files.end(), ".."));

    // sort in alphabetical order
    std::sort(files.begin(), files.end());

    return files;
}
#else
#include <dirent.h>
std::vector<std::string> files_in_directory(std::string directory)
{
    std::vector<std::string> files;

    // open directory
    DIR *dir;
    dir = opendir(directory.c_str());
    if (dir == NULL)
        return files;

    // get file names
    struct dirent *ent;
    while ((ent = readdir(dir)) != NULL)
        files.push_back(ent->d_name);
    closedir(dir);

    // delete current and parent directories
    files.erase(std::find(files.begin(), files.end(), "."));
    files.erase(std::find(files.begin(), files.end(), ".."));

    // sort in alphabetical order
    std::sort(files.begin(), files.end());

    return files;
}
#endif  // _WIN32
Burak
  • 2,251
  • 1
  • 16
  • 33
  • With C++17 we should use `std::filesystem::directory_iterator` and similar. – 0xC0000022L Sep 22 '21 at 10:27
  • @0xC0000022L Sure. This is a cross-platform solution for those who do not have c++17 support. – Burak Sep 22 '21 at 11:14
  • This is hardly cross-platform. Alone the Windows implementation doesn't account for `_UNICODE` being defined. And besides this is going blow up in the face of of a user in really big directories. There's a reason for why most (underlying) APIs are already based on an iterator-model as opposed to fetching a huge list all at once. That said, this is certainly a start. But quite frankly I'd probably rewrite the Windows portion to behave like `readdir()` and friends as this means a single interface which is more flexible than the one you offer. – 0xC0000022L Sep 22 '21 at 12:08
  • 1
    @0xC0000022L Thanks for the feedback. I used this piece of code in my small projects where there are not much files, and the platform is either Windows or Ubuntu. The codes do not belong to me. (I should have referred the sources.) This is a simple solution to most situations. I posted this to refer later and share with the others. As C++17 is widely used nowadays, this post becomes no longer needed. However, if you think it is a good idea to keep a non-modern solution without 3rd party libraries, I encourge you to post a new answer in which case I will delete this one. – Burak Sep 22 '21 at 18:12
1

Simply in Linux use following ASCI C style code

#include <bits/stdc++.h>
#include <dirent.h>
using namespace std;

int main(){
    DIR *dpdf;
    struct dirent *epdf;
    dpdf = opendir("./");
    
    if (dpdf != NULL){
    while (epdf = readdir(dpdf)){
        cout << epdf->d_name << std::endl;
    }
    }
    closedir(dpdf);
    return 0;
}

Hope this helps!

Vaibhav Pallod
  • 466
  • 7
  • 21
0

Just something that I want to share and thank you for the reading material. Play around with the function for a bit to understand it. You may like it. e stood for extension, p is for path, and s is for path separator.

If the path is passed without ending separator, a separator will be appended to the path. For the extension, if an empty string is inputted then the function will return any file that does not have an extension in its name. If a single star was inputted than all files in the directory will be returned. If e length is greater than 0 but is not a single * then a dot will be prepended to e if e had not contained a dot at the zero position.

For a returning value. If a zero-length map is returned then nothing was found but the directory was open okay. If index 999 is available from the return value but the map size is only 1 then that meant there was a problem with opening the directory path.

Note that for efficiency, this function can be split into 3 smaller functions. On top of that, you can create a caller function that will detect which function it is going to call based on the input. Why is that more efficient? Said if you are going to grab everything that is a file, doing that method the subfunction that built for grabbing all the files will just grab all that are files and does not need to evaluate any other unnecessary condition everytime it found a file.

That would also apply to when you grab files that do not have an extension. A specific built function for that purpose would only evaluate for weather if the object found is a file and then whether or not if the name of the file has a dot in it.

The saving may not be much if you only read directories with not so much files. But if you are reading a mass amount of directory or if the directory has couple hundred thousands of files, it could be a huge saving.

#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <dirent.h>
#include <map>

std::map<int, std::string> getFile(std::string p, std::string e = "", unsigned char s = '/'){
    if ( p.size() > 0 ){
        if (p.back() != s) p += s;
    }
    if ( e.size() > 0 ){
        if ( e.at(0) != '.' && !(e.size() == 1 && e.at(0) == '*') ) e = "." + e;
    }

    DIR *dir;
    struct dirent *ent;
    struct stat sb;
    std::map<int, std::string> r = {{999, "FAILED"}};
    std::string temp;
    int f = 0;
    bool fd;

    if ( (dir = opendir(p.c_str())) != NULL ){
        r.erase (999);
        while ((ent = readdir (dir)) != NULL){
            temp = ent->d_name;
            fd = temp.find(".") != std::string::npos? true : false;
            temp = p + temp;

            if (stat(temp.c_str(), &sb) == 0 && S_ISREG(sb.st_mode)){
                if ( e.size() == 1 && e.at(0) == '*' ){
                    r[f] = temp;
                    f++;
                } else {
                    if (e.size() == 0){
                        if ( fd == false ){
                            r[f] = temp;
                            f++;
                        }
                        continue;
                    }

                    if (e.size() > temp.size()) continue;

                    if ( temp.substr(temp.size() - e.size()) == e ){
                        r[f] = temp;
                        f++;
                    }
                }
            }
        }

        closedir(dir);
        return r;
    } else {
        return r;
    }
}

void printMap(auto &m){
    for (const auto &p : m) {
        std::cout << "m[" << p.first << "] = " << p.second << std::endl;
    }
}

int main(){
    std::map<int, std::string> k = getFile("./", "");
    printMap(k);
    return 0;
}
Kevin Ng
  • 2,146
  • 1
  • 13
  • 18
0
#include<iostream>
#include <dirent.h>
using namespace std;
char ROOT[]={'.'};

void listfiles(char* path){
    DIR * dirp = opendir(path);
    dirent * dp;
    while ( (dp = readdir(dirp)) !=NULL ) {
         cout << dp->d_name << " size " << dp->d_reclen<<std::endl;
    }
    (void)closedir(dirp);
}

int main(int argc, char **argv)
{
    char* path;
    if (argc>1) path=argv[1]; else path=ROOT;

    cout<<"list files in ["<<path<<"]"<<std::endl;
    listfiles(path);

    return 0;
}
Stan Sokolov
  • 2,140
  • 1
  • 22
  • 23
0

Try scandir() from dirent.h

man scandir()

-1

This worked for me. It writes a file with just the names (no path) of all the files. Then it reads that txt file and prints it for you.

void DisplayFolderContent()
    {

        system("dir /n /b * > file_names.txt");
        char ch;
        std::fstream myStream("file_names.txt", std::fstream::in);
        while (myStream.get(ch))
        {
            std::cout << ch;
        }

    }
-6

Contribution to Cesar Alejandro Montero Orozco answer.

There is beauty in simplicity, and by adding the /s key, we also go through subdirectories.

system("dir /n /b /s * > file_names.txt");
jarikmesik
  • 19
  • 4