168 votes

Comment faire pour itérer sur chaque fichier/répertoire de manière récursive en C++ standard ?

Comment faire pour itérer sur chaque fichier/répertoire de manière récursive en C++ standard ?

0 votes

Vous pouvez examiner boost.filesystem boost.org/doc/libs/1_31_0/libs/filesystem/doc/index.htm

2 votes

2 votes

Cela devrait bientôt figurer dans la norme via le Système de fichiers TS avec le Itérateur de répertoire récursif

104voto

1800 INFORMATION Points 55907

En C++ standard, il n'y a techniquement aucun moyen de le faire puisque le C++ standard n'a aucune conception des répertoires. Si vous voulez élargir un peu votre champ d'action, vous pouvez utiliser la fonction Boost.FileSystem . Il 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;
}

7 votes

Le C++ n'a pas de concept de fichiers ? Et std::fstream ? Ou fopen ?

24 votes

Mise à jour en fonction de la dernière version de boost : Au cas où quelqu'un tomberait sur cette réponse, la dernière version de boost inclut une classe de commodité boost::recursive_directory_iterator, donc écrire la boucle ci-dessus avec un appel récursif explicite n'est plus nécessaire. Lien : boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/

1 votes

Vous devriez probablement remplacer itr->leaf() par itr->path().leaf() ('leaf' : n'est pas un membre de 'boost::filesystem::directory_entry')

52voto

smink Points 39640

Si vous utilisez l'API Win32, vous pouvez utiliser la fonction FindFirstFile y FindNextFile fonctions.

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

Pour une traversée récursive des répertoires, vous devez inspecter chaque WIN32_FIND_DATA.dwFileAttributs pour vérifier si le ATTRIBUT_FICHIER_RÉPERTOIRE est activé. Si le bit est activé, vous pouvez appeler récursivement la fonction avec ce répertoire. Vous pouvez également utiliser une pile pour obtenir le même effet qu'un appel récursif mais en évitant le débordement de la pile pour les arbres de chemins 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;
}

23 votes

Combien de temps il t'a fallu pour écrire ça ? Je pense que cela prendrait moins de temps de coller le C++ à Python et de le faire en une seule ligne.

4 votes

Il s'agit d'une solution agréable et non récursive (ce qui est parfois pratique !).

2 votes

Btw, si quelqu'un veut éditer légèrement le programme pour accepter un paramètre de ligne de commande argv[1] pour le chemin au lieu d'un paramètre codé en dur (" F:\\cvsrepos "), la signature de main(int, char) deviendrait wmain(int, wchar_t) comme ceci : int wmain(int argc, wchar_t *argv[])

35voto

Matthieu G Points 61

Vous pouvez rendre les choses encore plus simples avec la nouvelle fonction C++11 basé sur la gamme for y 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;
}

7 votes

Pas besoin de boost. L'OP a spécifiquement demandé du C++ standard.

28voto

Alex Points 378

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

Fragment de code de travail provenant 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;
}

7 votes

Cette routine n'est pas récursive.

2 votes

@TimCooper, bien sûr que non, dirent est spécifique à Posix.

2 votes

En fait, il hace travailler sur VC++ si vous obtenez un portage de dirent.h pour visual C++ par Tony Ronkko. C'est un logiciel libre. Je viens de l'essayer et ça marche.

11voto

mrvincenzo Points 1503

En plus de boost::filesystem mentionné ci-dessus, vous voudrez peut-être examiner wxWidgets::wxDir y Qt::QDir .

wxWidgets et Qt sont tous deux des cadres C++ à source ouverte et multiplateformes.

wxDir fournit un moyen flexible de parcourir les fichiers de manière récursive en utilisant Traverse() ou un plus simple GetAllFiles() fonction. De même, vous pouvez implémenter la traversée avec GetFirst() y GetNext() (je suppose que Traverse() et GetAllFiles() sont des wrappers qui utilisent éventuellement 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 itérer sur 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 à mettre en œuvre une traversée récursive.

Voici un exemple de code (tiré de aquí # Exemple 8-5) qui montre comment itérer sur 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;
}

1 votes

Doxygen utilise QT comme couche de compatibilité avec le système d'exploitation. Les outils de base n'utilisent pas du tout d'interface graphique, juste le répertoire (et d'autres composants).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X