Comment parcourir de manière récursive chaque fichier / répertoire en C ++ standard?

StackOverflow https://stackoverflow.com/questions/67273

  •  09-06-2019
  •  | 
  •  

Question

Comment parcourez-vous chaque fichier / répertoire de manière récursive en C ++ standard?

Était-ce utile?

La solution

En C ++ standard, techniquement, il n’ya aucun moyen de le faire car le C ++ standard n’a aucune conception de répertoires. Si vous souhaitez développer un peu votre réseau, vous pouvez utiliser Boost.FileSystem . Cela a été accepté pour inclusion dans TR2, ce qui vous donne la meilleure chance de garder votre implémentation aussi proche que possible de la norme.

Un exemple tiré directement du site Web:

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

Autres conseils

Si vous utilisez l'API Win32, vous pouvez utiliser les fonctions FindFirstFile et FindNextFile .

http://msdn.microsoft.com/en -us / library / aa365200 (VS.85) .aspx

Pour parcourir les répertoires de manière récursive, vous devez inspecter chaque WIN32_FIND_DATA.dwFileAttributes afin de vérifier si le bit FILE_ATTRIBUTE_DIRECTORY est activé. Si le bit est défini, vous pouvez appeler de manière récursive la fonction avec ce répertoire. Vous pouvez également utiliser une pile pour obtenir le même effet qu'un appel récursif, tout en évitant le débordement de pile pour les arbres de chemin très longs.

#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>

using namespace std;

bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATA ffd;
    wstring spec;
    stack<wstring> directories;

    directories.push(path);
    files.clear();

    while (!directories.empty()) {
        path = directories.top();
        spec = path + L"\\" + mask;
        directories.pop();

        hFind = FindFirstFile(spec.c_str(), &ffd);
        if (hFind == INVALID_HANDLE_VALUE)  {
            return false;
        } 

        do {
            if (wcscmp(ffd.cFileName, L".") != 0 && 
                wcscmp(ffd.cFileName, L"..") != 0) {
                if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    directories.push(path + L"\\" + ffd.cFileName);
                }
                else {
                    files.push_back(path + L"\\" + ffd.cFileName);
                }
            }
        } while (FindNextFile(hFind, &ffd) != 0);

        if (GetLastError() != ERROR_NO_MORE_FILES) {
            FindClose(hFind);
            return false;
        }

        FindClose(hFind);
        hFind = INVALID_HANDLE_VALUE;
    }

    return true;
}

int main(int argc, char* argv[])
{
    vector<wstring> files;

    if (ListFiles(L"F:\\cvsrepos", L"*", files)) {
        for (vector<wstring>::iterator it = files.begin(); 
             it != files.end(); 
             ++it) {
            wcout << it->c_str() << endl;
        }
    }
    return 0;
}

Vous pouvez simplifier encore les choses avec la nouvelle gamme C ++ 11 . for et Boost :

#include <boost/filesystem.hpp>

using namespace boost::filesystem;    
struct recursive_directory_range
{
    typedef recursive_directory_iterator iterator;
    recursive_directory_range(path p) : p_(p) {}

    iterator begin() { return recursive_directory_iterator(p_); }
    iterator end() { return recursive_directory_iterator(); }

    path p_;
};

for (auto it : recursive_directory_range(dir_path))
{
    std::cout << it << std::endl;
}

En C ++ 11/14 avec & "Système de fichiers TS &"; le <experimental/filesystem> en-tête et plage - for vous pouvez simplement faire ceci:

#include <experimental/filesystem>

using std::experimental::filesystem::recursive_directory_iterator;
...
for (auto& dirEntry : recursive_directory_iterator(myPath))
     cout << dirEntry << endl;

À partir de C ++ 17, std::filesystem fait partie de la bibliothèque standard et se trouve dans l'en-tête <filesystem> (non plus & "expérimental &";).

Une solution rapide consiste à utiliser la bibliothèque Dirent.h de C.

Fragment de code de travail de Wikipedia:

#include <stdio.h>
#include <dirent.h>

int listdir(const char *path) {
    struct dirent *entry;
    DIR *dp;

    dp = opendir(path);
    if (dp == NULL) {
        perror("opendir: Path does not exist or could not be read.");
        return -1;
    }

    while ((entry = readdir(dp)))
        puts(entry->d_name);

    closedir(dp);
    return 0;
}

En plus du système de fichiers boost :: mentionné ci-dessus, vous voudrez peut-être examiner wxWidgets :: wxDir et Qt :: QDir .

wxWidgets et Qt sont des infrastructures C ++ multi-plateformes en open source.

wxDir offre un moyen flexible de parcourir les fichiers de manière récursive à l'aide de Traverse() ou d'une fonction plus simple GetAllFiles(). De plus, vous pouvez implémenter le parcours avec les fonctions GetFirst() et GetNext() (je suppose que Traverse () et GetAllFiles () sont des wrappers qui utilisent finalement les fonctions GetFirst () et GetNext ()).

QDir permet d'accéder aux structures de répertoires et à leur contenu. Il existe plusieurs façons de parcourir les répertoires avec QDir. Vous pouvez parcourir le contenu du répertoire (y compris les sous-répertoires) avec QDirIterator qui a été instancié avec l'indicateur QDirIterator :: Subdirectories. Une autre méthode consiste à utiliser la fonction GetEntryList () de QDir et à implémenter un parcours récursif.

Voici un exemple de code (extrait de ici # exemple 8-5) cela montre comment parcourir tous les sous-répertoires.

#include <qapplication.h>
#include <qdir.h>
#include <iostream>

int main( int argc, char **argv )
{
    QApplication a( argc, argv );
    QDir currentDir = QDir::current();

    currentDir.setFilter( QDir::Dirs );
    QStringList entries = currentDir.entryList();
    for( QStringList::ConstIterator entry=entries.begin(); entry!=entries.end(); ++entry) 
    {
         std::cout << *entry << std::endl;
    }
    return 0;
}

Boost :: filesystem fournit recursive_directory_iterator, ce qui est très pratique pour cette tâche:

#include "boost/filesystem.hpp"
#include <iostream>

using namespace boost::filesystem;

recursive_directory_iterator end;
for (recursive_directory_iterator it("./"); it != end; ++it) {
    std::cout << *it << std::endl;                                    
}

Vous pouvez utiliser ftw(3) ou nftw(3) pour parcourir une hiérarchie de système de fichiers. C ou C ++ sur les systèmes POSIX .

Vous n'avez pas. La norme C ++ n'a pas de concept de répertoires. Il appartient à l’implémentation de transformer une chaîne en descripteur de fichier. Le contenu de cette chaîne et son mappage dépendent du système d'exploitation. N'oubliez pas que C ++ peut être utilisé pour écrire ce système d'exploitation. Il est donc utilisé à un niveau où il n'est pas encore défini comment itérer dans un répertoire (car vous écrivez le code de gestion de répertoire).

Consultez la documentation de votre API de système d'exploitation pour savoir comment procéder. Si vous devez être portable, vous devrez disposer de #ifdef s pour divers systèmes d'exploitation.

Vous auriez probablement intérêt à utiliser boost ou le système de fichiers expérimental de c ++ 14. SI vous analysez un répertoire interne (utilisé pour que votre programme stocke les données après sa fermeture), créez ensuite un fichier d'index contenant un index du contenu. En passant, vous aurez probablement besoin d'utiliser boost dans le futur, donc si vous ne l'avez pas installé, installez-le! Deuxièmement, vous pouvez utiliser une compilation conditionnelle, par exemple:

#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif

Le code de chaque cas provient de https://stackoverflow.com/a/67336/7077165

#ifdef POSIX //unix, linux, etc.
#include <stdio.h>
#include <dirent.h>

int listdir(const char *path) {
    struct dirent *entry;
    DIR *dp;

    dp = opendir(path);
    if (dp == NULL) {
        perror("opendir: Path does not exist or could not be read.");
        return -1;
    }

    while ((entry = readdir(dp)))
        puts(entry->d_name);

    closedir(dp);
    return 0;
}
#endif
#ifdef WINDOWS
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>

using namespace std;

bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATA ffd;
    wstring spec;
    stack<wstring> directories;

    directories.push(path);
    files.clear();

    while (!directories.empty()) {
        path = directories.top();
        spec = path + L"\\" + mask;
        directories.pop();

        hFind = FindFirstFile(spec.c_str(), &ffd);
        if (hFind == INVALID_HANDLE_VALUE)  {
            return false;
        } 

        do {
            if (wcscmp(ffd.cFileName, L".") != 0 && 
                wcscmp(ffd.cFileName, L"..") != 0) {
                if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    directories.push(path + L"\\" + ffd.cFileName);
                }
                else {
                    files.push_back(path + L"\\" + ffd.cFileName);
                }
            }
        } while (FindNextFile(hFind, &ffd) != 0);

        if (GetLastError() != ERROR_NO_MORE_FILES) {
            FindClose(hFind);
            return false;
        }

        FindClose(hFind);
        hFind = INVALID_HANDLE_VALUE;
    }

    return true;
}
#endif
//so on and so forth.

Vous devez appeler des fonctions spécifiques au système d’exploitation pour la traversée du système de fichiers, comme open() et readdir(). La norme C ne spécifie aucune fonction liée au système de fichiers.

Vous n'avez pas. Le standard C ++ n'expose pas à la notion de répertoire. Plus précisément, cela ne donne aucun moyen de lister tous les fichiers d’un répertoire.

Un bidouillage horrible serait d’utiliser des appels system () et d’analyser les résultats. La solution la plus raisonnable consisterait à utiliser une sorte de bibliothèque multiplate-forme telle que Qt ou même POSIX .

Nous sommes en 2019. Nous avons la système de fichiers dans la bibliothèque standard située à C++. Le Filesystem library fournit des fonctionnalités pour effectuer des opérations sur les systèmes de fichiers et leurs composants, tels que les chemins d'accès, les fichiers normaux et les répertoires.

Une lien contient une note importante si vous envisagez des problèmes de portabilité. Il dit:

  

Les fonctionnalités de la bibliothèque du système de fichiers peuvent ne pas être disponibles si un système de fichiers hiérarchique n'est pas accessible pour la mise en œuvre ou s'il ne fournit pas les fonctionnalités nécessaires. Certaines fonctionnalités peuvent ne pas être disponibles si elles ne sont pas prises en charge par le système de fichiers sous-jacent (le système de fichiers FAT, par exemple, manque de liens symboliques et interdit la création de plusieurs liens en dur). Dans ce cas, les erreurs doivent être signalées.

La bibliothèque de système de fichiers a été initialement développée sous la forme boost.filesystem, a été publiée sous la spécification technique ISO / IEC TS 18822: 2015 et a finalement été fusionnée avec ISO C ++ à partir de C ++ 17. L’implémentation de boost est actuellement disponible sur davantage de compilateurs et de plates-formes que la bibliothèque C ++ 17.

@ adi-shavit a répondu à cette question lorsqu'il faisait partie de std :: experimental et il a mis à jour cette réponse en 2017. Je souhaite donner plus de détails sur la bibliothèque et donner un exemple plus détaillé.

std :: filesystem :: recursive_directory_iterator est un LegacyInputIterator que itère sur les éléments directory_entry d'un répertoire et, de manière récursive, sur les entrées de tous les sous-répertoires. L'ordre des itérations n'est pas spécifié, sauf que chaque entrée de répertoire n'est visitée qu'une seule fois.

Si vous ne souhaitez pas effectuer une itération récursive sur les entrées des sous-répertoires, directory_iterator doit être utilisé.

Les deux itérateurs renvoient un objet de directory_entry . directory_entry possède diverses fonctions de membre utiles telles que is_regular_file, is_directory, is_socket, is_symlink etc. La fonction de membre path() retourne un objet de std :: système_fichiers :: chemin et peut être utilisé pour obtenir file extension, filename, root name.

Considérons l'exemple ci-dessous. J'ai utilisé Ubuntu et l'ai compilé sur le terminal avec

g ++ exemple.cpp --std = c ++ 17 -lstdc ++ fs -Wall

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

void listFiles(std::string path)
{
    for (auto& dirEntry: std::filesystem::recursive_directory_iterator(path)) {
        if (!dirEntry.is_regular_file()) {
            std::cout << "Directory: " << dirEntry.path() << std::endl;
            continue;
        }
        std::filesystem::path file = dirEntry.path();
        std::cout << "Filename: " << file.filename() << " extension: " << file.extension() << std::endl;

    }
}

int main()
{
    listFiles("./");
    return 0;
}

Si vous êtes sous Windows, vous pouvez utiliser FindFirstFile avec l'API FindNextFile. Vous pouvez utiliser FindFileData.dwFileAttributes pour vérifier si un chemin donné est un fichier ou un répertoire. Si c’est un répertoire, vous pouvez répéter l’algorithme de manière récursive.

Ici, j'ai mis en place un code qui répertorie tous les fichiers sur une machine Windows.

http://dreams-soft.com/projects/traverse-directory

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top