web-dev-qa-db-fra.com

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

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

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

489
samoz

Dans les tâches simples et petites, je n'utilise pas de 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;
}

Il ne s'agit que d'un petit fichier d'en-tête. Il fait la plupart des choses simples dont vous avez besoin sans utiliser une approche basée sur des modèles, telle que boost (aucune infraction, j'aime boost!).

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

UPDATE 2017:

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

#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;
}
686
Peter Parker

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

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

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

236
Shreevardhan

Malheureusement, la norme C++ ne définit pas de manière standard l'utilisation de fichiers et de dossiers de cette manière. 

Comme il n’existe pas de méthode multiplate-forme, la meilleure solution consiste à utiliser une bibliothèque telle que le module de fichiers boost .

Méthode de renforcement de plateforme croisée:

La fonction suivante, à partir d'un chemin de répertoire et d'un nom de fichier, recherche récursivement le nom du fichier dans le répertoire et ses sous-répertoires, en 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::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 de la page de rappel mentionnée ci-dessus.


Pour les systèmes Unix/Linux: 

Vous pouvez utiliser opendir / readdir / closedir

Un exemple de code recherchant l’entrée «name» dans un répertoire 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 l'API Win32 FindFirstFile / FindNextFile / FindClose functions.

L'exemple C++ suivant montre une utilisation minimale de 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);
   }
}

Code source des pages msdn ci-dessus.

219
Brian R. Bondy

Une seule fonction suffit, vous n'avez pas besoin d'utiliser une bibliothèque tierce (pour 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: comme mentionné par @Sebastian, vous pouvez changer *.* en *.ext afin d’obtenir uniquement les fichiers EXT (c’est-à-dire d’un type spécifique) dans ce répertoire.

75
herohuyongtao

Pour une solution en C uniquement, veuillez vérifier ceci. Cela nécessite seulement 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:

  • C'est portable - enveloppe POSIX dirent 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
  • C'est du C90, donc même des compilateurs très anciens peuvent l'utiliser
46
congusbongus

Je recommande d'utiliser glob avec cet emballage réutilisable. Il génère un vector<string> correspondant aux chemins de fichier qui correspondent au modèle glob:

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

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

vector<string> files = globVector("./*");
27
Chris Redford

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

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

La sortie est comme:

file1.txt
file2.dat
21
Bad

Pourquoi ne pas utiliser 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;
}
17
Meekohi

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

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

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 de la structure dir

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

Essayez boost pour la méthode x-platform

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

ou utilisez simplement vos fichiers spécifiques à votre système d'exploitation.

10
Tim

Découvrez cette classe qui utilise l'API win32. Créez simplement une instance en fournissant la variable foldername à partir de laquelle vous souhaitez que la liste soit appelée, puis appelez la méthode getNextFile pour obtenir la prochaine variable filename du répertoire. Je pense qu'il a besoin de windows.h et 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;
    }

};
8
robertvarga

Manuel GNU FTW

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

Aussi, parfois, il est bon d'aller directement à la source (jeu de mots). Vous pouvez en apprendre beaucoup en examinant 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

Cela ne concerne peut-être pas Windows, mais plusieurs méthodes d’utilisation des variantes Unix peuvent être utilisées.

J'espère que cela pourra aider...

6
Homer6
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;
}
4
JasonYen2205

J'espère que ce code vous aidera.

#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;
}
3
Yas

vous pouvez obtenir tous les fichiers directement dans votre répertoire racine en utilisant std :: experimental :: filesystem :: directory_iterator (). Ensuite, lisez le nom de ces 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;
}
2
ducPham

Cela fonctionne pour moi. Je suis désolé si je ne me souviens pas de la source. Cela provient probablement d'une page de manuel.

#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/");
}
2
ENHering

La réponse de Shreevardhan fonctionne très bien. Mais si vous voulez l’utiliser dans c ++ 14, apportez une modification namespace fs = experimental::filesystem;

c'est à dire.,

#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;
}
2
Venkat Vinay

Cette implémentation réalise votre objectif en remplissant de manière dynamique 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;
}
2
Giacomo Marciani

Cette réponse devrait fonctionner pour les utilisateurs Windows qui ont eu du mal à le faire fonctionner avec Visual Studio avec l'une des autres réponses.

  1. Téléchargez le fichier dirent.h à partir de la page github. Mais il est préférable d’utiliser simplement le fichier Raw dirent.h et de suivre les étapes ci-dessous (c’est ainsi que je l’ai fait fonctionner).

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

    Fichier Dirent Raw: Fichier Dirent.h Raw

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

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

  4. Incluez "dirent.h" dans votre code.

  5. Placez la méthode void filefinder() ci-dessous dans votre code et appelez-la à partir de votre fonction main ou modifiez la fonction de la façon dont vous souhaitez l'utiliser.

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

Le système l'appelle!

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

Ensuite, il suffit de lire le fichier.

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

1
Catalyst

J'ai essayé de suivre l'exemple donné dans les deuxréponses et il convient de noter qu'il apparaît que si std::filesystem::directory_entry a été modifié pour ne pas surcharger l'opérateur <<. Au lieu de std::cout << p << std::endl;, j'ai dû utiliser les éléments suivants pour pouvoir compiler et le faire fonctionner:

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

essayer de passer p seul à std::cout << a entraîné une erreur de surcharge manquante.

0
StarKiller4011

Comme les fichiers et les sous-répertoires d'un répertoire sont généralement stockés dans une arborescence, un moyen intuitif consiste à utiliser l'algorithme DFS pour les parcourir de manière récursive. Voici un exemple dans le système d’exploitation Windows utilisant les fonctions de fichier 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<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);
}
0
tkingcer

Juste quelque chose que je veux partager et merci pour le matériel de lecture. Jouez un peu avec la fonction pour la comprendre. Vous pouvez aimer ça. e correspondait à extension, p à chemin et s à séparateur de chemin. 

Si le chemin est passé sans séparateur de fin, un séparateur sera ajouté au chemin. Pour l'extension, si une chaîne vide est entrée, la fonction renverra tout fichier ne portant pas d'extension dans son nom. Si une seule étoile a été entrée, tous les fichiers du répertoire seront retournés. Si e longueur est supérieure à 0 mais n'est pas un simple *, un point sera ajouté à e si e ne contenait pas de point à la position zéro.

Pour une valeur de retour. Si une carte de longueur nulle est retournée, rien n'a été trouvé, mais le répertoire était ouvert, d'accord. Si l'index 999 est disponible à partir de la valeur de retour mais que la taille de la carte n'est que de 1, cela signifiait qu'il y avait un problème avec l'ouverture du chemin du répertoire.

Notez que par souci d'efficacité, cette fonction peut être divisée en 3 fonctions plus petites. En plus de cela, vous pouvez créer une fonction appelant qui détectera la fonction à appeler en fonction de l'entrée. Pourquoi est-ce plus efficace? Si vous voulez récupérer tout ce qui est un fichier, utilisez cette méthode pour que la sous-fonction permettant de récupérer tous les fichiers ne contienne que tous les fichiers et n'a pas besoin d'évaluer toute autre condition inutile chaque fois qu'un fichier est trouvé. 

Cela s’appliquerait également lorsque vous récupérerez des fichiers n’ayant pas d’extension. Une fonction spécifique construite à cet effet n'évalue la météo que si l'objet trouvé est un fichier, puis si le nom du fichier contient un point.

L'enregistrement risque de ne pas être important si vous ne lisez que des répertoires contenant moins de fichiers. Mais si vous lisez un grand nombre de répertoires ou si le répertoire contient quelques centaines de milliers de fichiers, cela pourrait représenter une énorme économie.

#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;
}
0
Kevin Ng