Come posso ottenere l'elenco dei file in una directory usando C o C ++?


593

Come posso determinare l'elenco dei file in una directory dal mio codice C o C ++?

Non mi è permesso eseguire il lscomando e analizzare i risultati dal mio programma.


7
Questo è un duplicato di 609236
chrish


1
@chrish - Sì, ma questo ha il classico "Non posso eseguire" ls "! È esattamente come mi sentirei il 1 ° anno di Informatica. ; D <3 x
James Bedford,

1
C e C ++ non sono la stessa lingua. Pertanto, la procedura per eseguire questa attività sarà diversa in entrambe le lingue. Scegline uno e ri-tag di conseguenza.
MD XF

2
E nessuno di questi linguaggi (tranne C ++ dal C ++ 17) ha anche il concetto di una directory, quindi è probabile che qualsiasi risposta dipenda dal tuo sistema operativo o da qualsiasi libreria di astrazione che potresti utilizzare.
Toby Speight,

Risposte:


810

In piccole e semplici attività non utilizzo boost, utilizzo dirent.h che è disponibile anche per 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;
}

È solo un piccolo file di intestazione e fa la maggior parte delle cose semplici di cui hai bisogno senza usare un grande approccio basato su template come boost (senza offesa, mi piace boost!).

L'autore del livello di compatibilità di Windows è Toni Ronkko. In Unix, è un'intestazione standard.

AGGIORNAMENTO 2017 :

In C ++ 17 v'è ora un modo ufficiale per file di elenco di file system: std::filesystem. C'è una risposta eccellente da Shreevardhan di seguito con questo codice sorgente:

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

5
@ArtOfWarfare: tinydir non è stato nemmeno creato alla risposta a questa domanda. Inoltre è un wrapper per dirent (POSIX) e FindFirstFile (Windows), mentre dirent.h esegue il wrapping di dirent per Windows. Penso che sia un gusto personale, ma dirent.h si sente più come uno standard
Peter Parker,

7
@JoshC: poiché * ent è solo un puntatore restituito della rappresentazione interna. chiudendo la directory eliminerai anche * ent. Dato che * ent è solo per la lettura, penso che questo sia un design sano.
Peter Parker,

42
la gente diventa reale !! questa è una domanda del 2009 e non ha nemmeno menzionato VS. Quindi non criticare il fatto che il tuo IDE proprietario completo (anche se abbastanza carino) non supporti standard di sistema operativo secolari. Inoltre la mia risposta ha detto che è "disponibile" per Windows, non "incluso" in nessun IDE da ora e per tutte le volte ... Sono abbastanza sicuro che puoi scaricare Dirent e metterlo in alcuni include dir e voilà che è.
Peter Parker,

6
La risposta è fuorviante. Dovrebbe iniziare con: " ... io uso dirent.h , per il quale esiste anche un livello di compatibilità open source di Windows ".
Rustyx,

10
Con C ++ 14 c'è std::experimental::filesystem, con C ++ 17 c'è std::filesystem. Vedi la risposta di Shreevardhan di seguito. Quindi non sono necessarie librerie di terze parti.
Roi Danton,

347

C ++ 17 ora ha a std::filesystem::directory_iterator, che può essere usato come

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

Inoltre, std::filesystem::recursive_directory_iteratorpuò iterare anche le sottodirectory.


28
AFAIK può essere utilizzato anche in C ++ 14, ma è ancora in fase sperimentale: namespace fs = std::experimental::filesystem;. Sembra funzionare bene però.
PeterK,

7
Questa dovrebbe essere la risposta preferita per l'uso corrente (a partire da C ++ 17)
diod verde

4
Prestare attenzione quando si passa std::filesystem::patha std::cout, le virgolette sono incluse nell'output. Per evitarlo, aggiungi .string()al percorso una conversione esplicita anziché implicita (qui std::cout << p.string() << std::endl;). Esempio: coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
Roi Danton

2
Che dire dei caratteri NON ASCII nei nomi dei file? Non dovrebbe std::wstringessere usato o qual è il tipo dall'iteratore?
anddero,

2
Non sono sicuro di essere solo in questo, ma senza collegamento a -lstdc++fs, otterrei un SIGSEGV (Address boundary error). Non sono riuscito a trovare da nessuna parte nella documentazione che ciò fosse necessario, e neanche il linker ha dato alcun indizio. Questo ha funzionato per entrambi g++ 8.3.0e clang 8.0.0-3. Qualcuno ha qualche idea su dove cose come questa sono specificate nei documenti / specifiche?
swalog,

229

Sfortunatamente lo standard C ++ non definisce in questo modo un modo standard di lavorare con file e cartelle.

Poiché non esiste un metodo multipiattaforma, il modo migliore multipiattaforma è utilizzare una libreria come il modulo boost filesystem .

Metodo di boost multipiattaforma:

La seguente funzione, dato un percorso di directory e un nome di file, cerca ricorsivamente nella directory e nelle sue sottodirectory il nome del file, restituendo un valore booleano e, in caso di successo, il percorso del file trovato.

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

Fonte dalla pagina boost di cui sopra.

Per i sistemi basati su Unix / Linux:

Puoi usare opendir / readdir / closedir .

Il codice di esempio che cerca in una directory la voce `` nome '' è:

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;

Codice sorgente dalle pagine man sopra.

Per sistemi basati su Windows:

È possibile utilizzare le funzioni FindFirstFile / FindNextFile / FindClose dell'API Win32 .

L'esempio C ++ seguente mostra un uso minimo di 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);
   }
}

Codice sorgente dalle pagine msdn sopra.


Utilizzo:FindFirstFile(TEXT("D:\\IMAGE\\MYDIRECTORY\\*"), &findFileData);
Константин Ван l'

7
Con C ++ 14 c'è std::experimental::filesystem, con C ++ 17 c'è std::filesystem, che hanno funzionalità simili a boost (le librerie sono derivate da boost). Vedi la risposta di Shreevardhan di seguito.
Roi Danton,


90

Una funzione è sufficiente, non è necessario utilizzare alcuna libreria di terze parti (per 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;
}

PS: come menzionato da @Sebastian, è possibile passare *.*a *.extper ottenere solo i file EXT (cioè di un tipo specifico) in quella directory.


20
Questa soluzione è specifica per la piattaforma. Questo è il motivo per cui hai bisogno di librerie di terze parti.
kraxor,

9
@kraxor Sì, funziona solo in Windows, ma OP non chiede mai di avere una soluzione multipiattaforma. A proposito, preferisco sempre scegliere qualcosa senza usare le librerie di 3 ° (se possibile).
Herohuyongtao,

7
@herohuyongtao OP non ha mai specificato una piattaforma e dare una soluzione fortemente dipendente dalla piattaforma a una domanda generica può essere fuorviante. (E se esistesse una soluzione a una riga che funziona solo su PlayStation 3? È una buona risposta qui?) Vedo che hai modificato la tua risposta per affermare che funziona solo su Windows, immagino che vada bene così.
kraxor,

1
@herohuyongtao OP ha menzionato che non può analizzare ls, il che significa che probabilmente è su unix .. comunque, buona risposta per Windows.
Thomas,

2
Ho finito per usare a std::vector<std::wstring>e poi fileName.c_str()invece di un vettore di stringhe, che non si sarebbe compilato.
PerryC,

51

Per una soluzione unica in C, controlla questo. Richiede solo un'intestazione aggiuntiva:

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

Alcuni vantaggi rispetto ad altre opzioni:

  • È portatile: avvolge POSIX dirent e Windows FindFirstFile
  • Usa readdir_rdove disponibile, il che significa che è (solitamente) sicuro per i thread
  • Supporta Windows UTF-16 tramite le stesse UNICODEmacro
  • È C90 quindi anche i compilatori molto antichi possono usarlo

2
Suggerimento molto bello. Non l'ho ancora testato su un computer Windows, ma funziona
perfettamente

La libreria non supporta std :: string, quindi non puoi passare file.c_str () a tinydir_open. In questo caso, genera l'errore C2664 durante la compilazione su msvc 2015.
Stepan Yakovenko,

33

Consiglio di utilizzare globquesto involucro riutilizzabile. Genera un percorso vector<string>di file corrispondente che si adatta al modello glob:

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

Che può quindi essere chiamato con un normale modello jolly di sistema come:

vector<string> files = globVector("./*");

2
Prova che glob () restituisce zero.
Camille Goudeseune,

Vorrei usare glob.h come mi hai raccomandato. Ma ancora, non posso includere il file .h: dice No such file or directory. Puoi dirmi come risolvere questo problema, per favore?
Tofuw,

Si noti che questa routine supera solo un livello (nessuna ricorsione). Inoltre, non esegue un controllo rapido per determinare se si tratta di un file o di una directory, che è possibile eseguire facilmente passando GLOB_TILDEda GLOB_TILDE | GLOB_MARKe quindi controllando la presenza di percorsi che terminano con una barra. Dovrai apportare entrambe le modifiche se ne hai bisogno.
Volomike,

Questa è multipiattaforma compatibile?
Nikhil Augustine,

Purtroppo non è possibile trovare file nascosti in modo uniforme tramite glob.
LmTinyToon,

23

Ecco un codice molto semplice C++11nell'uso della boost::filesystemlibreria per ottenere i nomi dei file in una directory (esclusi i nomi delle cartelle):

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

L'output è come:

file1.txt
file2.dat

Ciao, e dove posso trovare questa libreria?
Alexander Leon VI,

2
@Alexander De Leon: puoi trovare questa libreria sul loro sito boost.org , leggere prima la guida introduttiva , quindi utilizzare la loro boost::filesystemlibreria boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm
Bad

23

Perché non usare 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;
}

Questa potrebbe essere una risposta migliore se spieghi le inclusioni richieste.
Volomike

2
Prova che glob () restituisce zero!
orbitcowboy,

Questo va bene quando conosci il file che stai cercando come * .txt
Kemin Zhou

20

Penso che sotto lo snippet possa essere usato per elencare tutti i file.

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

Segue la struttura della direttrice strutt

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

Vorrei questo.
avvio automatico

10

Prova boost per il metodo x-platform

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

o semplicemente usa i file specifici del tuo sistema operativo.


Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia. - Dalla recensione
ice1000,

@ ice1000 Seriamente? Questo Q&A è del 2009
Tim,

8

Dai un'occhiata a questa classe che utilizza l'API win32. Basta costruire un'istanza fornendo il foldernameda cui si desidera l'elenco, quindi chiamare il getNextFilemetodo per ottenere il successivo filenamedalla directory. Penso che abbia bisogno windows.he 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;
    }

};

6

Manuale GNU FTW

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

Inoltre, a volte è bene andare direttamente alla fonte (gioco di parole intenzionale). Puoi imparare molto guardando le viscere di alcuni dei comandi più comuni in Linux. Ho creato un semplice mirror dei coreutils di GNU su github (per la lettura).

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

Forse questo non si rivolge a Windows, ma un certo numero di casi di utilizzo delle varianti Unix può essere ottenuto utilizzando questi metodi.

Spero che aiuti...


5

La risposta di Shreevardhan funziona alla grande. Ma se vuoi usarlo in c ++ 14, fai solo una modificanamespace fs = experimental::filesystem;

vale a dire,

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

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

3

Spero che questo codice ti aiuti.

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

4
-1. string2wchar_trestituisce l'indirizzo di una variabile locale. Inoltre, dovresti probabilmente usare i metodi di conversione disponibili in WinAPI invece di scriverne di tuoi.
Daniel Kamil Kozar,

3

Questa implementazione realizza il tuo scopo, riempiendo dinamicamente una matrice di stringhe con il contenuto della directory specificata.

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

Come lo definirei? Ricevo i segfault quando provo ad eseguire questa funzione sul primo ifblocco. Lo chiamo conchar **list; int numItems; exploreDirectory("/folder",list, numItems);
Hal T

2

Questo funziona per me. Mi dispiace se non ricordo la fonte. Probabilmente proviene da una pagina man.

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

2

puoi ottenere tutti i file diretti nella tua directory principale usando std :: sperimentale :: filesystem :: directory_iterator (). Quindi, leggi il nome di questi file di percorso.

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

2

Questa risposta dovrebbe funzionare per gli utenti Windows che hanno avuto problemi a farlo funzionare con Visual Studio con una qualsiasi delle altre risposte.

  1. Scarica il file dirent.h dalla pagina github. Ma è meglio usare solo il file dirent.h crudo e seguire i miei passaggi di seguito (è come l'ho fatto funzionare).

    Pagina Github per dirent.h per Windows: pagina Github per dirent.h

    File Dirent grezzo: File Dirent.h grezzo

  2. Vai al tuo progetto e aggiungi un nuovo elemento ( Ctrl+ Shift+ A). Aggiungi un file header (.h) e chiamalo dirent.h.

  3. Incolla il codice Raw dirent.h nell'intestazione.

  4. Includi "dirent.h" nel tuo codice.

  5. Inserisci il void filefinder()metodo seguente nel codice e chiamalo dalla tua mainfunzione o modifica la funzione come desideri utilizzarla.

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

1

Sistema chiamalo!

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

Quindi leggi il file.

EDIT: questa risposta dovrebbe essere considerata un trucco, ma funziona davvero (anche se in modo specifico della piattaforma) se non si ha accesso a soluzioni più eleganti.


7
Non mi è permesso eseguire il comando 'ls' e analizzare i risultati dal mio programma. Sapevo che ci sarebbe stato qualcuno che avrebbe inviato qualcosa del genere ...
yyny,

1

Poiché i file e le sottodirectory di una directory sono generalmente memorizzati in una struttura ad albero, un modo intuitivo è utilizzare l'algoritmo DFS per attraversare ricorsivamente ciascuno di essi. Ecco un esempio nel sistema operativo Windows utilizzando le funzioni di file di base in io.h. È possibile sostituire queste funzioni in un'altra piattaforma. Quello che voglio esprimere è che l'idea di base di DFS soddisfa perfettamente questo 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);
}

1

Ho provato a seguire l'esempio fornito in entrambe le risposte e potrebbe valere la pena notare che sembra che std::filesystem::directory_entrysia stato modificato per non avere un sovraccarico <<dell'operatore. Invece di std::cout << p << std::endl;dover usare quanto segue per essere in grado di compilare e farlo funzionare:

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

il tentativo di passare pda solo ha std::cout <<provocato un errore di sovraccarico mancante.


1

Sulla base di ciò che ha pubblicato herohuyongtao e di alcuni altri post:

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

Qual è il tipo di input previsto di FindFirstFile?

Come convertire wstring in stringa?

Questa è una soluzione Windows.

Dato che volevo passare in std :: string e restituire un vettore di stringhe, ho dovuto fare un paio di conversioni.

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

Se sai che utilizzerai solo multibyte, potresti usare WIN32_FIND_DATAA, FindFirstFileAe FindNextFileA. Quindi non sarà necessario convertire il risultato in multibyte o Input in unicode.
Kiran Thilak il

0

Solo qualcosa che voglio condividere e grazie per il materiale di lettura. Gioca con la funzione per un po 'per capirlo. Potrebbe piacerti indica l'estensione, p indica il percorso e s indica il separatore del percorso.

Se il percorso viene passato senza terminare il separatore, verrà aggiunto un separatore al percorso. Per l'estensione, se viene immessa una stringa vuota, la funzione restituirà qualsiasi file che non ha un'estensione nel suo nome. Se è stata immessa una singola stella, verranno restituiti tutti i file nella directory. Se la lunghezza e è maggiore di 0 ma non è un singolo *, un punto verrà anteposto a e se e non avesse contenuto un punto nella posizione zero.

Per un valore di ritorno. Se viene restituita una mappa di lunghezza zero, non è stato trovato nulla ma la directory è stata aperta correttamente. Se l'indice 999 è disponibile dal valore restituito ma la dimensione della mappa è solo 1, significa che si è verificato un problema con l'apertura del percorso della directory.

Per efficienza, questa funzione può essere suddivisa in 3 funzioni più piccole. Inoltre, puoi creare una funzione chiamante che rileverà quale funzione chiamerà in base all'input. Perché è più efficiente? Detto se hai intenzione di afferrare tutto ciò che è un file, facendo quel metodo la sottofunzione creata per afferrare tutti i file prenderà semplicemente tutti quei file e non ha bisogno di valutare altre condizioni non necessarie ogni volta che trova un file.

Ciò si applicherebbe anche a quando si acquisiscono file che non hanno estensione. Una specifica funzione incorporata a tale scopo valuterà il tempo solo se l'oggetto trovato è un file e quindi se il nome del file contiene o meno un punto.

Il salvataggio potrebbe non essere molto se leggi solo directory con non così tanti file. Ma se stai leggendo una grande quantità di directory o se la directory ha duecentomila file, potrebbe essere un enorme risparmio.

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

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

-1

Questo ha funzionato per me. Scrive un file con solo i nomi (nessun percorso) di tutti i file. Quindi legge quel file txt e lo stampa per te.

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

    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.