it-swarm-es.tech

¿Cómo se repite recursivamente en cada archivo/directorio en C++ estándar?

¿Cómo se repite recursivamente en cada archivo/directorio en C++ estándar?

99
robottobor

En C++ estándar, técnicamente no hay forma de hacerlo ya que C++ estándar no tiene una concepción de directorios. Si desea expandir su red un poco, le recomendamos que utilice Boost.FileSystem . Esto ha sido aceptado para su inclusión en TR2, por lo que le brinda 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;
}
93
1800 INFORMATION

Si usa la API de Win32, puede usar FindFirstFile y FindNextFile functions.

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

Para un recorrido recursivo de directorios, debe inspeccionar cada WIN32_FIND_DATA.dwFileAttributes para verificar si el FILE_ATTRIBUTE_DIRECTORY bit está establecido. Si el bit está establecido, entonces puede recurrir recursivamente a la función con ese directorio. Alternativamente, puede usar una pila para proporcionar el mismo efecto que 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;
}
42
Jorge Ferreira

Puede hacerlo aún más simple con el nuevo C++ 11 range 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;
}
31
Matthieu G

En C++ 11/14 con el "Sistema de archivos TS", el <experimental/filesystem> header and range -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;

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

27
Adi Shavit

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

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

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

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

wxDir proporciona una forma flexible de recorrer los archivos de forma recursiva utilizando Traverse() o una función GetAllFiles() más simple. También puede implementar el recorrido con las funciones GetFirst() y GetNext() (supongo que Traverse () y GetAllFiles () son envoltorios que eventualmente usan las funciones GetFirst () y GetNext ()).

QDir proporciona acceso a las estructuras de directorios y sus contenidos. Hay varias formas de atravesar directorios con QDir. Puede iterar sobre el contenido del directorio (incluidos los subdirectorios) con QDirIterator que se creó una instancia con el indicador 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 en 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;
}
10
mrvincenzo

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

Puede usar ftw(3) O nftw(3) para recorrer una jerarquía del sistema de archivos en C o C++ en POSIX systems.

4
leif

Usted no El estándar C++ no tiene concepto de directorios. Depende de la implementación convertir una cadena en un identificador de archivo. El contenido de esa cadena y su asignación 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 aún no se ha definido cómo recorrer un directorio (porque está escribiendo el código de administración de directorios).

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

3
Matthew Scouten

Probablemente sea mejor con el sistema experimental de archivos boost o c ++ 14.SIestá analizando un directorio interno (es decir, utilizado por su programa para almacenar datos después de que el programa se cerró), luego cree un archivo de índice que tenga un índice del contenido del archivo. Por cierto, probablemente necesitarás usar boost en el futuro, así que si no lo tienes instalado, ¡instálalo! En segundo lugar, puedes usar una compilación condicional, por ejemplo:

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

El código para cada caso se toma 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.
3
ndrewxie

Debe 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
John Millikin

Usted no El estándar C++ no expone al concepto de un directorio. Específicamente, no proporciona ninguna forma de enumerar todos los archivos en un directorio.

Un truco horrible sería usar las 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
shoosh