¿Cómo puedo obtener la lista de archivos en un directorio usando C o C ++?

593

¿Cómo puedo determinar la lista de archivos en un directorio desde mi código C o C ++?

No tengo permiso para ejecutar el lscomando y analizar los resultados desde mi programa.

Samoz
fuente
77
Este es un duplicado de 609236
chrish
1
@chrish - Sí, pero este tiene el clásico "¡No tengo permitido ejecutar el 'ls'"! Es exactamente como me sentiría el primer año de informática. ; D <3 x
James Bedford
1
C y C ++ no son el mismo lenguaje. Por lo tanto, el procedimiento para realizar esta tarea será diferente en ambos idiomas. Elija uno y vuelva a etiquetar en consecuencia.
MD XF
2
Y ninguno de esos lenguajes (aparte de C ++ desde C ++ 17) tiene un concepto de directorio, por lo que es probable que cualquier respuesta dependa de su sistema operativo o de cualquier biblioteca de abstracción que pueda estar utilizando.
Toby Speight

Respuestas:

810

En tareas pequeñas y simples, no uso boost, uso dirent.h, que también está disponible para 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;
}

Es solo un pequeño archivo de encabezado y hace la mayoría de las cosas simples que necesita sin usar un gran enfoque basado en plantillas como boost (sin ofender, ¡me gusta boost!).

El autor de la capa de compatibilidad de Windows es Toni Ronkko. En Unix, es un encabezado estándar.

ACTUALIZACIÓN 2017 :

En C ++ 17 ahora hay una forma oficial de archivos de lista de su sistema de archivos: std::filesystem. Hay una excelente respuesta de Shreevardhan a continuación con este código fuente:

#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;
}
Peter Parker
fuente
55
@ArtOfWarfare: tinydir ni siquiera se creó cuando se respondió esta pregunta. También es un contenedor alrededor de dirent (POSIX) y FindFirstFile (Windows), mientras que dirent.h simplemente envuelve dirent para Windows. Creo que es un gusto personal, pero directo. Me siento más como un estándar
Peter Parker
77
@JoshC: porque * ent es solo un puntero devuelto de la representación interna. al cerrar el directorio, también eliminará el * ent. Como * ent es solo para leer, creo que este es un diseño sensato.
Peter Parker
42
la gente se vuelve real !! Esta es una pregunta de 2009 y ni siquiera ha mencionado VS. Por lo tanto, no critique que su IDE patentado completo (aunque bastante bueno) no admite estándares de sistema operativo centenarios. También mi respuesta dice que está "disponible" para Windows, no "incluido" en ningún IDE desde ahora y para siempre ... Estoy bastante seguro de que puede descargarlo directamente y ponerlo en algún directorio de inclusión y listo.
Peter Parker
66
La respuesta es engañosa. Debería comenzar con: " ... utilizo dirent.h , para el cual también existe una capa de compatibilidad de código abierto de Windows ".
rustyx
10
Con C ++ 14 existe std::experimental::filesystem, con C ++ 17 existe std::filesystem. Ver la respuesta de Shreevardhan a continuación. Por lo tanto, no necesita bibliotecas de terceros.
Roi Danton el
347

C ++ 17 ahora tiene un std::filesystem::directory_iterator, que se puede usar como

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

Además, también std::filesystem::recursive_directory_iteratorpuede iterar los subdirectorios.

Shreevardhan
fuente
28
AFAIK también se puede utilizar en C ++ 14, pero hay todavía es experimental: namespace fs = std::experimental::filesystem;. Aunque parece funcionar bien.
PeterK
77
Esta debería ser la respuesta preferida para el uso actual (comenzando con C ++ 17)
diodo verde
44
Preste atención al pasar std::filesystem::patha std::cout, las comillas se incluyen en la salida. Para evitar eso, agregue .string()a la ruta para hacer una conversión explícita en lugar de implícita (aquí std::cout << p.string() << std::endl;). Ejemplo: coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
Roi Danton el
2
¿Qué pasa con los caracteres NO ASCII en los nombres de archivo? ¿No std::wstringdebería usarse o cuál es el tipo del iterador?
anddero
2
No estoy seguro de si estoy solo en esto, pero sin vincularme -lstdc++fs, obtendría un SIGSEGV (Address boundary error). No pude encontrar en ninguna parte de la documentación que esto fuera necesario, y el enlazador tampoco dio ninguna pista. Esto funcionó para ambos g++ 8.3.0y clang 8.0.0-3. ¿Alguien tiene alguna idea de dónde se especifica este tipo de cosas en los documentos / especificaciones?
swalog
229

Desafortunadamente, el estándar C ++ no define una forma estándar de trabajar con archivos y carpetas de esta manera.

Dado que no existe una forma multiplataforma, la mejor forma multiplataforma es utilizar una biblioteca como el módulo de sistema de archivos boost .

Método de impulso multiplataforma:

La siguiente función, dada una ruta de directorio y un nombre de archivo, busca de forma recursiva el nombre del archivo en el directorio y sus subdirectorios, devuelve un bool y, si tiene éxito, la ruta al archivo que se encontró.

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

Fuente de la página de impulso mencionada anteriormente.

Para sistemas basados ​​en Unix / Linux:

Puede usar opendir / readdir / closedir .

El código de muestra que busca en un directorio la entrada `` nombre '' es:

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;

Código fuente de las páginas de manual anteriores.

Para sistemas basados ​​en Windows:

Puede utilizar las API de Win32 FindFirstFile / FindNextFile / FindClose funciones.

El siguiente ejemplo de C ++ muestra un uso mínimo 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);
   }
}

Código fuente de las páginas msdn anteriores.

Brian R. Bondy
fuente
Uso:FindFirstFile(TEXT("D:\\IMAGE\\MYDIRECTORY\\*"), &findFileData);
Константин Ван
77
Con C ++ 14 existe std::experimental::filesystem, con C ++ 17 existe std::filesystem, que tiene una funcionalidad similar a boost (las libs se derivan de boost). Ver la respuesta de Shreevardhan a continuación.
Roi Danton el
Para Windows, consulte docs.microsoft.com/en-us/windows/desktop/FileIO/… para más detalles
FindOutIslamNow
90

Una función es suficiente, no necesita usar ninguna biblioteca de terceros (para 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;
}

PD: como mencionó @Sebastian, puede cambiar *.*a *.extpara obtener solo los archivos EXT (es decir, de un tipo específico) en ese directorio.

herohuyongtao
fuente
20
Esta solución es específica de la plataforma. Esa es la razón por la que necesita bibliotecas de terceros.
kraxor
99
@kraxor Sí, solo funciona en Windows, pero OP nunca pide tener una solución multiplataforma. Por cierto, siempre prefiero elegir algo sin usar terceras bibliotecas (si es posible).
herohuyongtao
77
@herohuyongtao OP nunca especificó una plataforma, y ​​dar una solución altamente dependiente de la plataforma a una pregunta genérica puede ser engañoso. (¿Qué pasa si hay una solución de una línea que solo funciona en PlayStation 3? ¿Es una buena respuesta aquí?) Veo que editó su respuesta para indicar que solo funciona en Windows, supongo que está bien de esta manera.
kraxor
1
@herohuyongtao OP mencionó que no puede analizar ls, lo que significa que probablemente esté en Unix ... de todos modos, es una buena respuesta para Windows.
Thomas
2
Terminé usando a std::vector<std::wstring>y luego en fileName.c_str()lugar de un vector de cadenas, que no se compilaría.
PerryC
51

Para una solución única en C, por favor revise esto. Solo requiere un encabezado adicional:

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

Algunas ventajas sobre otras opciones:

  • Es portátil: envuelve POSIX dirent y Windows FindFirstFile
  • Utiliza readdir_rdonde esté disponible, lo que significa que (generalmente) es seguro para subprocesos
  • Admite Windows UTF-16 a través de las mismas UNICODEmacros
  • Es C90, por lo que incluso los compiladores muy antiguos pueden usarlo
congusbongus
fuente
2
Muy buena sugerencia. Todavía no lo he probado en una computadora con Windows, pero funciona de manera brillante en OS X.
ArtOfWarfare
La biblioteca no admite std :: string, por lo que no puede pasar file.c_str () a tinydir_open. Da el error C2664 durante la compilación en msvc 2015 en este caso.
Stepan Yakovenko
33

Recomiendo usar globcon este envoltorio reutilizable. Genera una ruta vector<string>correspondiente al archivo que se ajusta al patrón global:

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

Que luego se puede llamar con un patrón comodín del sistema normal como:

vector<string> files = globVector("./*");
Chris Redford
fuente
2
Prueba que glob () devuelve cero.
Camille Goudeseune
Me gustaría usar glob.h como me lo recomendó. Pero aún así, no puedo incluir el archivo .h: dice No such file or directory. ¿Me puede decir cómo resolver este problema, por favor?
Tofuw
Tenga en cuenta que esta rutina solo tiene un nivel de profundidad (sin recursividad). Asimismo, no hacer una rápida comprobación para determinar si se trata de un archivo o directorio, lo que se puede hacer fácilmente por el cambio GLOB_TILDEcon GLOB_TILDE | GLOB_MARKy luego la comprobación de caminos que terminan en una barra. Tendrá que hacer cualquier modificación si lo necesita.
Volomike
¿Es compatible esta plataforma cruzada?
Nikhil Augustine
Lamentablemente no puede encontrar archivos uniformemente ocultos a través de glob.
LmTinyToon
23

Aquí hay un código muy simple al C++11usar la boost::filesystembiblioteca para obtener nombres de archivos en un directorio (excluyendo los nombres de carpetas):

#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 salida es como:

file1.txt
file2.dat
Malo
fuente
Hola, ¿y dónde puedo obtener esta biblioteca?
Alexander Leon VI
2
@Alexander De Leon: Puede obtener esta biblioteca en su sitio boost.org , lea primero la guía de inicio, luego use su boost::filesystembiblioteca boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm
Malo
23

¿Por qué no usar 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;
}
Meekohi
fuente
Esta podría ser una mejor respuesta si explica los requisitos incluidos.
Volomike
2
¡Prueba que glob () devuelve cero!
orbitcowboy
Esto es bueno cuando conoce el archivo que está buscando, como * .txt
Kemin Zhou
20

Creo que el siguiente fragmento se puede usar para enumerar todos los archivos.

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

A continuación se muestra la estructura de la estructura dirent.

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 */
};
Shrikant
fuente
Me gustaría este
autoarranque el
10

Prueba el método boost para x-platform

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

o simplemente use su archivo de sistema operativo específico.

Tim
fuente
Si bien este enlace puede responder la pregunta, es mejor incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia. - De la opinión
ice1000
@ ice1000 ¿En serio? Este Q&A es de 2009
Tim
8

Echa un vistazo a esta clase que usa la API win32. Simplemente construya una instancia proporcionando el nombre foldernamedel cual desea el listado y luego llame al getNextFilemétodo para obtener el siguiente filenamedel directorio. Creo que necesita windows.hy 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;
    }

};
robertvarga
fuente
6

GNU Manual FTW

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

Además, a veces es bueno ir directamente a la fuente (juego de palabras). Puedes aprender mucho mirando las entrañas de algunos de los comandos más comunes en Linux. He configurado un espejo simple de los coreutils de GNU en github (para leer).

https://github.com/homer6/gnu_coreutils/blob/master/src/ls.c

Tal vez esto no aborde Windows, pero se pueden tener varios casos de uso de variantes de Unix utilizando estos métodos.

Espero que ayude...

Homero6
fuente
5

La respuesta de Shreevardhan funciona muy bien. Pero si quieres usarlo en c ++ 14 solo haz un cambionamespace fs = experimental::filesystem;

es decir,

#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;
}
Venkat Vinay
fuente
4
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;
}
JasonYen2205
fuente
3

Espero que este código te ayude.

#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;
}
Yas
fuente
44
-1. string2wchar_tdevuelve la dirección de una variable local. Además, probablemente debería usar los métodos de conversión disponibles en WinAPI en lugar de escribir los suyos propios.
Daniel Kamil Kozar
3

Esta implementación cumple su propósito, llenando dinámicamente una serie de cadenas con el contenido del directorio especificado.

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;
}
Giacomo Marciani
fuente
¿Cómo llamaría esto? Recibo segfaults cuando intento ejecutar esta función en el primer ifbloque. Lo llamo conchar **list; int numItems; exploreDirectory("/folder",list, numItems);
Hal T
2

Esto funciona para mi. Lo siento si no puedo recordar la fuente. Probablemente sea de una página de manual.

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

puede obtener todos los archivos directos en su directorio raíz utilizando std :: experimental :: filesystem :: directory_iterator (). Luego, lea el nombre de estos archivos de ruta.

#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;
}
ducPham
fuente
2

Esta respuesta debería funcionar para los usuarios de Windows que han tenido problemas para que esto funcione con Visual Studio con cualquiera de las otras respuestas.

  1. Descargue el archivo dirent.h de la página de github. Pero es mejor usar el archivo Raw dirent.h y seguir mis pasos a continuación (así es como lo hice funcionar).

    Página de Github para dirent.h para Windows: página de Github para dirent.h

    Archivo Dirent sin procesar: Archivo dirent.h sin procesar

  2. Vaya a su proyecto y agregue un nuevo elemento ( Ctrl+ Shift+ A). Agregue un archivo de encabezado (.h) y asígnele el nombre dirent.h.

  3. Pegue el código del archivo Raw dirent.h en su encabezado.

  4. Incluya "dirent.h" en su código.

  5. Ponga el siguiente void filefinder()método en su código y llámelo desde su mainfunción o edite la función como quiera usarlo.

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

El sistema lo llama!

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

Entonces solo lee el archivo.

EDITAR: esta respuesta debe considerarse un truco, pero realmente funciona (aunque sea de forma específica para la plataforma) si no tiene acceso a soluciones más elegantes.

Catalizador
fuente
77
No se me permite ejecutar el comando 'ls' y analizar los resultados desde mi programa. Sabía que habría alguien que enviaría algo así ...
yyny
1

Dado que los archivos y subdirectorios de un directorio generalmente se almacenan en una estructura de árbol, una forma intuitiva es usar el algoritmo DFS para recorrer recursivamente cada uno de ellos. Aquí hay un ejemplo en el sistema operativo Windows utilizando funciones básicas de archivo en io.h. Puede reemplazar estas funciones en otra plataforma. Lo que quiero expresar es que la idea básica de DFS cumple perfectamente con este problema.

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

Traté de seguir el ejemplo dado en ambas respuestas y podría valer la pena señalar que parece que std::filesystem::directory_entryse ha cambiado para no tener una sobrecarga del <<operador. En lugar de std::cout << p << std::endl;tener que usar lo siguiente para poder compilar y hacer que funcione:

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

tratando de pasar ppor sí solo para dar como std::cout <<resultado un error de sobrecarga faltante.

StarKiller4011
fuente
1

Sobre la base de lo que herohuyongtao publicó y algunas otras publicaciones:

http://www.cplusplus.com/forum/general/39766/

¿Cuál es el tipo de entrada esperado de FindFirstFile?

¿Cómo convertir wstring en cadena?

Esta es una solución de Windows.

Como quería pasar std :: string y devolver un vector de cadenas, tuve que hacer un par de conversiones.

#include <string>
#include <Windows.h>
#include <vector>
#include <locale>
#include <codecvt>

std::vector<std::string> listFilesInDir(std::string path)
{
    std::vector<std::string> names;
    //Convert string to wstring
    std::wstring search_path = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(path);
    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)) 
            {
                //convert from wide char to narrow char array
                char ch[260];
                char DefChar = ' ';
                WideCharToMultiByte(CP_ACP, 0, fd.cFileName, -1, ch, 260, &DefChar, NULL);
                names.push_back(ch);
            }
        } 
        while (::FindNextFile(hFind, &fd));
        ::FindClose(hFind);
    }
    return names;
}
tzg
fuente
Si sabe que solo usará multibyte, podría usar WIN32_FIND_DATAA, FindFirstFileAy FindNextFileA. Entonces no habrá necesidad de convertir el resultado a multibyte o la entrada a unicode.
Kiran Thilak
0

Es algo que quiero compartir y gracias por el material de lectura. Juega un poco con la función para entenderla. Te puede gustar. e representaba la extensión, p es para la ruta y s es para el separador de ruta.

Si la ruta se pasa sin terminar el separador, se agregará un separador a la ruta. Para la extensión, si se ingresa una cadena vacía, la función devolverá cualquier archivo que no tenga una extensión en su nombre. Si se ingresó una sola estrella, se devolverán todos los archivos del directorio. Si la longitud de e es mayor que 0 pero no es un solo *, entonces un punto se antepondrá a e si e no hubiera contenido un punto en la posición cero.

Por un valor de retorno. Si se devuelve un mapa de longitud cero, entonces no se encontró nada, pero el directorio estaba abierto. Si el índice 999 está disponible desde el valor de retorno pero el tamaño del mapa es solo 1, eso significa que hubo un problema al abrir la ruta del directorio.

Tenga en cuenta que, para mayor eficiencia, esta función se puede dividir en 3 funciones más pequeñas. Además de eso, puede crear una función de llamada que detectará a qué función va a llamar en función de la entrada. ¿Por qué es eso más eficiente? Dijo que si va a capturar todo lo que es un archivo, al hacer ese método, la subfunción que se creó para capturar todos los archivos simplemente capturará todos los archivos y no necesita evaluar ninguna otra condición innecesaria cada vez que encuentra un archivo.

Eso también se aplicaría cuando tomas archivos que no tienen una extensión. Una función construida específica para ese propósito solo evaluaría el clima si el objeto encontrado es un archivo y luego si el nombre del archivo tiene un punto o no.

El ahorro puede no ser mucho si solo lee directorios con no tantos archivos. Pero si está leyendo una cantidad masiva de directorio o si el directorio tiene un par de cientos de miles de archivos, podría ser un gran ahorro.

#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;
}
Kevin Ng
fuente
0
#include<iostream>
#include <dirent.h>
using namespace std;
char ROOT[]={'.'};

void listfiles(char* path){
    DIR * dirp = opendir(path);
    dirent * dp;
    while ( (dp = readdir(dirp)) !=NULL ) {
         cout << dp->d_name << " size " << dp->d_reclen<<std::endl;
    }
    (void)closedir(dirp);
}

int main(int argc, char **argv)
{
    char* path;
    if (argc>1) path=argv[1]; else path=ROOT;

    cout<<"list files in ["<<path<<"]"<<std::endl;
    listfiles(path);

    return 0;
}
Stan Sokolov
fuente
-1

Esto funcionó para mí. Escribe un archivo con solo los nombres (sin ruta) de todos los archivos. Luego lee ese archivo txt y lo imprime por usted.

void DisplayFolderContent()
    {

        system("dir /n /b * > file_names.txt");
        char ch;
        std::fstream myStream("file_names.txt", std::fstream::in);
        while (myStream.get(ch))
        {
            std::cout << ch;
        }

    }
Cesar Alejandro Montero Orozco
fuente