¿Cómo iterar a través de cada archivo/directorio recursivamente en C++estándar?


¿Cómo iterar a través de cada archivo/directorio recursivamente en C++estándar?

Author: Peter Mortensen, 2008-09-16

14 answers

En C++ estándar, técnicamente no hay manera de hacer esto ya que C++ estándar no tiene una concepción de directorios. Si desea expandir su red un poco, es posible que desee mirar el uso de Boost.Sistema de archivos . Esto ha sido aceptado para su inclusión en TR2, por lo que esto le da la mejor oportunidad de mantener su implementación lo más cerca posible del estándar.

Un ejemplo, tomado directamente del sitio 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;
}
 91
Author: 1800 INFORMATION,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-15 23:36:46

Si usa la API Win32, puede usar las funciones FindFirstFile y FindNextFile.

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

Para el recorrido recursivo de directorios debe inspeccionar cada WIN32_FIND_DATA.dwFileAttributes para comprobar si el bit FILE_ATTRIBUTE_DIRECTORY está configurado. Si el bit está configurado, entonces puede llamar recursivamente a la función con ese directorio. Alternativamente, puede usar una pila para proporcionar el mismo efecto de una llamada recursiva pero evitando el desbordamiento de pila para árboles de ruta muy largos.

#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;
}
 35
Author: Jorge Ferreira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-09 16:50:34

Puede hacerlo aún más simple con el nuevo C++11 basado en rango 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;
}
 30
Author: Matthieu G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-09 13:56:27

Una solución rápida es usar C's Dirent.h biblioteca.

Fragmento de código de trabajo 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;
}
 22
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-09 13:54:01

En C++11/14 con el "Sistema de archivos TS", el <experimental/filesystem> encabezado y rango-for simplemente puede hacer esto:

#include <experimental/filesystem>

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

UPDATE: A partir de C++17, std::filesystem es parte de la biblioteca estándar y se puede encontrar en el encabezado <filesystem> (ya no es "experimental").

 18
Author: Adi Shavit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-09 10:43:22

Además del sistema de archivos boost::mencionado anteriormente, es posible que desee examinar wxWidgets::wxDir y Qt::QDir.

Tanto wxWidgets como Qt son frameworks C++ multiplataforma de código abierto.

wxDir proporciona una forma flexible de recorrer archivos recursivamente usando Traverse() o una función GetAllFiles() más simple. También puedes implementar el traversal con las funciones GetFirst() y GetNext() (asumo que Traverse () y GetAllFiles() son envoltorios que eventualmente usan getFirst () y GetNext () functions).

QDir proporciona acceso a las estructuras de directorios y sus contenidos. Hay varias maneras de recorrer directorios con QDir. Puede iterar sobre el contenido del directorio (incluidos los subdirectorios) con QDirIterator que fue instanciado con la bandera Qdiriterator::Subdirectories. Otra forma es usar la función GetEntryList() de QDir e implementar un recorrido recursivo.

Aquí está el código de ejemplo (tomado de aquí # Ejemplo 8-5) que muestra cómo iterar sobre todos los subdirectorios.

#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;
}
 9
Author: mrvincenzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-21 09:18:33

Es posible que desee examinar boost.sistema de archivos

Http://www.boost.org/doc/libs/1_31_0/libs/filesystem/doc/index.htm

 5
Author: Doug T.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-15 21:44:30

Boost:: filesystem proporciona recursive_directory_iterator, que es bastante conveniente para esta tarea:

#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;                                    
}
 5
Author: DikobrAz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-13 16:18:00

Puede utilizar ftw(3) o nftw(3) para recorrer una jerarquía de sistemas de archivos en C o C++ en sistemas POSIX .

 3
Author: leif,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-09 13:57:23

Necesita llamar a funciones específicas del sistema operativo para el recorrido del sistema de archivos, como open() y readdir(). El estándar C no especifica ninguna función relacionada con el sistema de archivos.

 2
Author: John Millikin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-15 21:42:41

No. El estándar C++ no tiene concepto de directorios. Depende de la implementación convertir una cadena en un controlador de archivo. El contenido de esa cadena y a qué se asigna depende del sistema operativo. Tenga en cuenta que C++ se puede usar para escribir ese sistema operativo, por lo que se usa en un nivel en el que preguntar cómo iterar a través de un directorio aún no está definido (porque está escribiendo el código de administración de directorios).

Consulte la documentación de la API de su sistema operativo para saber cómo hacer esto. Si usted necesita ser portátil, usted tendrá que tener un montón de #ifdef s para varios sistemas operativos.

 2
Author: Matthew Scouten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-09 13:51:29

Si está en Windows, puede usar FindFirstFile junto con la API FindNextFile. Puede utilizar FindFileData.dwFileAttributes para comprobar si una ruta dada es un archivo o un directorio. Si es un directorio, puede repetir recursivamente el algoritmo.

Aquí, he reunido un código que enumera todos los archivos en una máquina Windows.

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

 2
Author: Ibrahim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-17 15:34:41

Probablemente sería mejor con boost o el sistema de archivos experimental de c++14. SI está analizando un directorio interno (ie. utilizado para su programa para almacenar datos después de que el programa fue cerrado), a continuación, hacer un archivo de índice que tiene un índice del contenido del archivo. Por cierto, es probable que tenga que utilizar boost en el futuro, así que si usted no tiene instalado, instalarlo! En segundo lugar, podrías usar una compilación condicional:

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

Código en 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.
 2
Author: ndrewxie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-08 02:29:57

No. C++ estándar no expone al concepto de directorio. Específicamente no da ninguna manera de enumerar todos los archivos en un directorio.

Un truco horrible sería usar llamadas a system() y analizar los resultados. La solución más razonable sería utilizar algún tipo de biblioteca multiplataforma como Qt o incluso POSIX .

 1
Author: shoosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-09 13:48:30