Comment puis-je obtenir la liste des fichiers dans un répertoire en utilisant C ou C ++?

Comment puis-je déterminer la liste des fichiers dans un répertoire à partir de mon code C ou C ++?

Je ne suis pas autorisé à exécuter la commande 'ls' et à parsingr les résultats de mon programme.

Dans les tâches petites et simples, je n’utilise pas Boost, j’utilise dirent.h qui est également disponible pour Windows:

 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; } 

C’est juste un petit fichier d’en-tête et fait la plupart des choses simples dont vous avez besoin sans utiliser une approche basée sur des modèles comme boost (pas de délit, j’aime boost!).

L’auteur de la couche de compatibilité Windows est Toni Ronkko. Dans Unix, c’est un en-tête standard.

MISE À JOUR 2017 :

En C ++ 17, il existe désormais un moyen officiel de lister les fichiers de votre système de fichiers: std::filesystem . Il y a une excellente réponse de Shreevardhan ci-dessous avec ce code source:

 #include  #include  #include  namespace fs = std::filesystem; int main() { std::ssortingng path = "/path/to/directory"; for (auto & p : fs::directory_iterator(path)) std::cout << p << std::endl; } 

Envisagez de voter sa réponse, si vous utilisez l’approche C ++ 17.

Malheureusement, le standard C ++ ne définit pas une manière standard de travailler avec des fichiers et des dossiers de cette manière.

Comme il n’y a pas de plate-forme croisée, la meilleure façon de multiplier les plates-formes consiste à utiliser une bibliothèque telle que le module de système de fichiers boost .

Méthode de renforcement de plate-forme croisée:

La fonction suivante, avec un chemin de répertoire et un nom de fichier, recherche récursivement dans le répertoire et ses sous-répertoires le nom du fichier, renvoyant un booléen et, en cas de succès, le chemin du fichier trouvé.

 bool find_file(const path & dir_path, // in this directory, const std::ssortingng & 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 de la page de boost mentionnée ci-dessus.


Pour les systèmes basés sur Unix / Linux:

Vous pouvez utiliser opendir / readdir / closedir .

L’exemple de code qui recherche un répertoire pour l’entrée «name» est:

  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; 

Code source des pages de manuel ci-dessus.


Pour un système Windows:

vous pouvez utiliser les fonctions FindFirstFile / FindNextFile / FindClose de l’ API Win32.

L’exemple C ++ suivant vous montre une utilisation minimale de FindFirstFile.

 #include  #include  #include  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); } } 

Code source des pages msdn ci-dessus.

C ++ 17 a maintenant un std::filesystem::directory_iterator , qui peut être utilisé comme

 #include  #include  #include  namespace fs = std::filesystem; int main() { std::ssortingng path = "/path/to/directory"; for (const auto & p : fs::directory_iterator(path)) std::cout << p << std::endl; // "p" is the directory entry. Get the path with "p.path()". } 

De même, std::filesystem::recursive_directory_iterator peut également parcourir les sous-répertoires.

Une seule fonction suffit, vous n’avez pas besoin d’utiliser une bibliothèque tierce (pour Windows).

 #include  vector get_all_files_names_within_folder(ssortingng folder) { vector names; ssortingng 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.dwFileAtsortingbutes & FILE_ATTRIBUTE_DIRECTORY) ) { names.push_back(fd.cFileName); } }while(::FindNextFile(hFind, &fd)); ::FindClose(hFind); } return names; } 

PS: comme mentionné par @Sebastian, vous pouvez remplacer *.* *.ext afin d’obtenir uniquement les fichiers EXT (c’est-à-dire d’un type spécifique) dans ce répertoire.

Pour une solution C uniquement, veuillez vérifier ceci. Il ne nécessite qu’un en-tête supplémentaire:

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

Quelques avantages par rapport aux autres options:

  • Il est portable – encapsule le répertoire POSIX et Windows FindFirstFile
  • Il utilise readdir_r lorsqu’il est disponible, ce qui signifie qu’il est (généralement) threadsafe
  • Prend en charge Windows UTF-16 via les mêmes macros UNICODE
  • Il est C90 donc même les très anciens compilateurs peuvent l’utiliser

Je recommande d’utiliser glob avec cette enveloppe réutilisable. Il génère un vector correspondant aux chemins de fichiers correspondant au pattern glob:

 #include  #include  using std::vector; vector globVector(const ssortingng& pattern){ glob_t glob_result; glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result); vector files; for(unsigned int i=0;i 

Qui peut alors être appelé avec un modèle générique de système normal tel que:

 vector files = globVector("./*"); 

Voici un code très simple en C++11 utilisant boost::filesystem bibliothèque boost::filesystem pour obtenir des noms de fichiers dans un répertoire (à l’exception des noms de dossiers):

 #include  #include  #include  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().ssortingng() << endl; } else continue; } } 

La sortie est comme:

 file1.txt file2.dat 

Pourquoi ne pas utiliser glob() ?

 #include  glob_t glob_result; glob("/your_directory/*",GLOB_TILDE,NULL,&glob_result); for(unsigned int i=0; i 

Je pense que l’extrait ci-dessous peut être utilisé pour lister tous les fichiers.

 #include  #include  #include  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); } 

Voici la structure du struct dirent

 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 */ }; 

Essayer de booster la méthode x-platform

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

ou utilisez simplement les fichiers de votre système d’exploitation.

Découvrez cette classe qui utilise l’api win32. Construisez simplement une instance en fournissant le foldername de foldername depuis lequel vous souhaitez que la liste appelle la méthode getNextFile pour obtenir le filename de filename suivant dans le répertoire. Je pense qu’il a besoin de windows.h et de 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; } }; 

Manuel GNU FTW

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

En outre, parfois, il est bon d’aller directement à la source (jeu de mots). Vous pouvez apprendre beaucoup en regardant les entrailles de certaines des commandes les plus courantes sous Linux. J’ai mis en place un simple miroir des coreutils de GNU sur github (pour la lecture).

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

Peut-être que cela ne concerne pas Windows, mais un certain nombre de cas d’utilisation de variantes Unix peuvent être obtenus en utilisant ces méthodes.

J’espère que cela pourra aider…

 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; } 

J’espère que ce code vous aidera.

 #include  #include  #include  #include  using namespace std; ssortingng wchar_t2ssortingng(const wchar_t *wchar) { ssortingng str = ""; int index = 0; while(wchar[index] != 0) { str += (char)wchar[index]; ++index; } return str; } wchar_t *ssortingng2wchar_t(const ssortingng &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 listFilesInDirectory(ssortingng directoryName) { WIN32_FIND_DATA FindFileData; wchar_t * FileName = ssortingng2wchar_t(directoryName); HANDLE hFind = FindFirstFile(FileName, &FindFileData); vector listFileNames; listFileNames.push_back(wchar_t2ssortingng(FindFileData.cFileName)); while (FindNextFile(hFind, &FindFileData)) listFileNames.push_back(wchar_t2ssortingng(FindFileData.cFileName)); return listFileNames; } void main() { vector listFiles; listFiles = listFilesInDirectory("C:\\*.txt"); for each (ssortingng str in listFiles) cout << str << endl; } 

Cette implémentation réalise votre objective, en remplissant dynamicment un tableau de chaînes avec le contenu du répertoire spécifié.

 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; } 

Cela fonctionne pour moi. Je suis désolé si je ne peux pas me souvenir de la source. C’est probablement une page de manuel.

 #include  int AnalizeDirectoryElement (const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf) { if (tflag == FTW_F) { std::ssortingng 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/"); } 

Vous pouvez obtenir tous les fichiers directs dans votre répertoire racine en utilisant std :: experimental :: filesystem :: directory_iterator (). Ensuite, lisez le nom de ces chemins.

 #include  #include  #include  #include  using namespace std; namespace fs = std::experimental::filesystem; void ShowListFile(ssortingng path) { for(auto &p: fs::directory_iterator(path)) /*get directory */ cout< 

Le système l’appelle!

 system( "dir /b /s /ad * > file_names.txt" ); 

Ensuite, lisez simplement le fichier.

EDIT: Cette réponse doit être considérée comme un hack, mais elle fonctionne vraiment (bien que de manière spécifique à une plate-forme) si vous n’avez pas access à des solutions plus élégantes.

Réponse Shreevardhan fonctionne très bien. Mais si vous voulez l’utiliser dans c ++ 14, créez simplement un namespace fs = experimental::filesystem; modification namespace fs = experimental::filesystem;

c’est à dire,

 #include  #include  #include  using namespace std; namespace fs = experimental::filesystem; int main() { ssortingng path = "C:\\splits\\"; for (auto & p : fs::directory_iterator(path)) cout << p << endl; int n; cin >> n; } 

Les fichiers et sous répertoires d’un répertoire étant généralement stockés dans une arborescence, une méthode intuitive consiste à utiliser l’algorithme DFS pour parcourir récursivement chacun d’entre eux. Voici un exemple du système d’exploitation Windows en utilisant les fonctions de fichiers de base dans io.h. Vous pouvez remplacer ces fonctions sur une autre plate-forme. Ce que je veux exprimer, c’est que l’idée de base de DFS répond parfaitement à ce problème.

 #include #include #include using namespace std; void TraverseFilesUsingDFS(const ssortingng& folder_path){ _finddata_t file_info; ssortingng 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); } 

Cette réponse devrait fonctionner pour les utilisateurs Windows qui ont des difficultés à utiliser Visual Studio avec l’une des autres réponses.

  1. Téléchargez le fichier dirent.h à partir de la page github. Mais mieux vaut simplement utiliser le fichier Raw dirent.h et suivre mes étapes ci-dessous (c’est comme ça que je l’ai fait fonctionner).

    Page Github pour dirent.h pour Windows: page Github pour dirent.h

    Fichier de Dirent brut: fichier dirent.h brut

  2. Accédez à votre projet et Ajouter un nouvel élément ( Ctrl + Maj + A ). Ajoutez un fichier d’en-tête (.h) et nommez-le dirent.h.

  3. Collez le code de fichier Raw dirent.h dans votre en-tête.

  4. Inclure “dirent.h” dans votre code.

  5. Placez la void filefinder() ci-dessous dans votre code et appelez-la à partir de votre fonction main ou modifiez-la comme vous voulez l’utiliser.

     #include  #include  #include "dirent.h" ssortingng 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  //std::cout << direntStruct->d_name << std::endl; //If you are using  } } closedir(directory); } 

Cela a fonctionné pour moi. Il écrit un fichier avec juste les noms (pas de chemin) de tous les fichiers. Ensuite, il lit ce fichier txt et l’imprime pour vous.

 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; } }