829

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.

asked Mar 4, 2009 at 19:35
8
  • 9
    This is a duplicate of 609236 Commented Mar 4, 2009 at 20:35
  • 1
    See also stat() error 'no such file or directory' when file name is returned by readdir(). Commented Dec 25, 2014 at 1:54
  • 5
    @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 Commented Oct 22, 2016 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. Commented Mar 2, 2017 at 21:58
  • 5
    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. Commented Feb 20, 2018 at 11:38

33 Answers 33

1
2
1128

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
1441 silver badge13 bronze badges
answered Mar 4, 2009 at 19:57
Sign up to request clarification or add additional context in comments.

23 Comments

@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
@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.
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.
The answer is misleading. It should begin with: "...I use dirent.h, for which a Windows open-source compatibility layer also exists ".
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.
|
534

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
3,2851 gold badge16 silver badges5 bronze badges
answered May 28, 2016 at 2:41

15 Comments

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.
This should be the preferred answer for current use (starting with C++17)
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: coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
What about NON-ASCII characters in file names? Shouldn't std::wstring be used or what's the type from the iterator?
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?
|
241

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
364k70 gold badges555 silver badges600 bronze badges
answered Mar 4, 2009 at 19:38

3 Comments

Usage: FindFirstFile(TEXT("D:\\IMAGE\\MYDIRECTORY\\*"), &findFileData);
With C++14 there is std::experimental::filesystem, with C++17 there is std::filesystem, which have similar functionality as boost (the libs are derived from boost). See answer of Shreevardhan below.
107

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.

answered Dec 30, 2013 at 20:56

16 Comments

This solution if platform-specific. That is the reason you need 3rd-party libraries.
@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 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.
@herohuyongtao OP mentioned he can't parse ls, meaning he is probably on unix.. anyway, good answer for Windows.
I ended up using a std::vector<std::wstring> and then fileName.c_str() instead of a vector of strings, which wouldn't compile.
|
62

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
answered Feb 4, 2013 at 1:06

3 Comments

Very nice suggestion. I haven't tested it on a windows computer yet but it works brilliantly on OS X.
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.
@StepanYakovenko the author stated clearly that "For a C only solution"
37

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("./*");
answered Jul 11, 2014 at 17:15

5 Comments

Test that glob() returns zero.
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 ?
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.
Is this cross-platform compatible?
Unfortunately you cannot find uniformly hidden files via glob.
34

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
20.8k28 gold badges101 silver badges143 bronze badges
answered Oct 23, 2013 at 6:36

3 Comments

I'd like this one.
This did the job for me in C++11 without having to use Boost etc. Good solution!
This was nice! In what order am I supposed to get the files?
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
answered Jun 25, 2015 at 16:51

3 Comments

Hi, and where can I get this library?
@Alexander De Leon: You can get this library at their site boost.org, read getting started guide first, then use their boost::filesystem library boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm
@Bad how would I change this to output the complete directory for each file. like I want D:/AnyFolder/file1.txt and so on?
26

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;
}
answered Feb 10, 2014 at 18:57

3 Comments

This could be a better answer if you explain the required includes.
Test that glob() returns zero!
This is good when you know the file you are looking for such as *.txt
12

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.

answered Mar 4, 2009 at 19:37

2 Comments

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
@ice1000 Seriously? This Q&A is from 2009
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,0923 gold badges27 silver badges32 bronze badges
answered Apr 8, 2011 at 16:48

1 Comment

Where will you close handle?
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...

answered Nov 11, 2012 at 21:35

Comments

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;
}
answered Mar 13, 2018 at 6:38

Comments

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;
}
answered Jan 14, 2021 at 1:38

Comments

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;
}
answered Mar 29, 2012 at 21:12

Comments

3

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;
}
answered Feb 19, 2012 at 18:22

1 Comment

-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.
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;
}
answered Sep 13, 2014 at 15:08

1 Comment

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);
3

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

answered Nov 16, 2021 at 15:45

Comments

2

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.

answered Jul 3, 2014 at 15:40

2 Comments

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...
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.
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/");
}
answered May 22, 2017 at 4:44

Comments

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;
}
answered Apr 20, 2018 at 18:04

Comments

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
50.1k32 gold badges118 silver badges143 bronze badges
answered Sep 4, 2018 at 0:24

Comments

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
8,0984 gold badges43 silver badges60 bronze badges
answered Sep 25, 2018 at 13:10

Comments

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();} );
}
answered Nov 18, 2022 at 7:59

Comments

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);
}
answered Apr 10, 2018 at 15:16

Comments

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;
}
answered Aug 30, 2019 at 15:46

2 Comments

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.
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, .. 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 ...
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
answered Jan 13, 2021 at 15:59

4 Comments

With C++17 we should use std::filesystem::directory_iterator and similar.
@0xC0000022L Sure. This is a cross-platform solution for those who do not have c++17 support.
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 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.
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!

answered Jul 19, 2022 at 8:01

Comments

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;
}
answered Sep 29, 2018 at 10:54

Comments

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;
}
answered Dec 10, 2019 at 1:45

Comments

1
2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.