Como posso obter a lista de arquivos em um diretório usando C ou C ++?

593

Como posso determinar a lista de arquivos em um diretório de dentro do meu código C ou C ++?

Não tenho permissão para executar o lscomando e analisar os resultados de dentro do meu programa.

samoz
fonte
7
Esta é uma duplicata de 609.236
ChrisH
1
@chrish - Sim, mas este aqui tem o clássico "Não tenho permissão para executar o 'ls'"! É exatamente como eu me sentiria no primeiro ano de Ciência da Computação. ; D <3 x
James Bedford
1
C e C ++ não são a mesma linguagem. Portanto, o procedimento para realizar esta tarefa será diferente nos dois idiomas. Por favor, escolha um e re-etiquete de acordo.
MD XF
2
E nenhuma dessas linguagens (exceto C ++ desde C ++ 17) tem o conceito de diretório - portanto, qualquer resposta provavelmente depende do seu sistema operacional ou de qualquer biblioteca de abstração que você possa estar usando.
precisa

Respostas:

809

Em tarefas pequenas e simples, não uso o boost, uso o dirent.h, que também está disponível para o 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;
}

É apenas um pequeno arquivo de cabeçalho e faz a maioria das coisas simples que você precisa, sem usar uma grande abordagem baseada em modelo como impulso (sem ofensa, eu gosto de aumento!).

O autor da camada de compatibilidade do Windows é Toni Ronkko. No Unix, é um cabeçalho padrão.

ATUALIZAÇÃO 2017 :

Em C ++ 17 há agora uma maneira oficial de arquivos de lista de seu sistema de arquivos: std::filesystem. Há uma excelente resposta de Shreevardhan abaixo com este código-fonte:

#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
fonte
5
@ArtOfWarfare: tinydir nem sequer foi criado quando esta pergunta foi respondida. Também é um invólucro em torno do dirent (POSIX) e FindFirstFile (Windows), enquanto o dirent.h apenas agrupa o dirent para janelas. Eu acho que é um gosto pessoal, mas dirent.h se sente mais como um padrão
Peter Parker
7
@ JoshC: porque * ent é apenas um ponteiro retornado da representação interna. fechando o diretório, você também eliminará o * ent. Como o * ent é apenas para leitura, acho que é um design sensato.
Peter Parker
42
as pessoas ficam reais !! Esta é uma pergunta de 2009 e nem sequer mencionou o VS. Portanto, não critique que seu IDE proprietário completo (embora bastante agradável) não esteja suportando padrões de sistema operacional com séculos de idade. Além disso, minha resposta disse que está "disponível" para o Windows, não "incluído" em qualquer IDE a partir de agora e para sempre ... Tenho certeza de que você pode baixar o dirent e colocá-lo em alguns incluem dir dir e pronto.
Peter Parker
6
A resposta é enganosa. Deve começar com: " ... eu uso o dirent.h , para o qual também existe uma camada de compatibilidade de código aberto do Windows ".
Rustyx
10
Com C ++ 14 existe std::experimental::filesystem, com C ++ 17 existe std::filesystem. Veja a resposta de Shreevardhan abaixo. Portanto, não há necessidade de bibliotecas de terceiros.
precisa saber é o seguinte
347

O C ++ 17 agora possui um std::filesystem::directory_iterator, que pode ser usado 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;
}

Além disso, também std::filesystem::recursive_directory_iteratorpode iterar os subdiretórios.

Shreevardhan
fonte
28
AFAIK também pode ser usado em C ++ 14, mas não é ainda experimental: namespace fs = std::experimental::filesystem;. Parece funcionar bem embora.
PeterK
7
Esta deve ser a resposta preferida para uso corrente (começando com C ++ 17)
diod verde
4
Preste atenção ao passar std::filesystem::pathpara std::cout, as aspas estão incluídas na saída. Para evitar isso, anexe .string()ao caminho para fazer uma conversão explícita em vez de implícita (aqui std::cout << p.string() << std::endl;). Exemplo: coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3
Roi Danton
2
E os caracteres NÃO ASCII nos nomes de arquivo? Não deve std::wstringser usado ou qual é o tipo do iterador?
anddero
2
Não tenho certeza se estou sozinho nisso, mas sem vincular a -lstdc++fs, eu obteria um SIGSEGV (Address boundary error). Não consegui encontrar em nenhum lugar da documentação que isso fosse necessário e o vinculador também não deu nenhuma pista. Isso funcionou para ambos g++ 8.3.0e clang 8.0.0-3. Alguém tem alguma idéia de onde essas coisas são especificadas nos documentos / especificações?
swalog 02/07/19
229

Infelizmente, o padrão C ++ não define uma maneira padrão de trabalhar com arquivos e pastas dessa maneira.

Como não existe uma maneira multiplataforma, a melhor maneira multiplataforma é usar uma biblioteca como o módulo de sistema de arquivos boost .

Método de aumento de plataforma cruzada:

A função a seguir, dado um caminho de diretório e um nome de arquivo, procura recursivamente o diretório e seus subdiretórios pelo nome do arquivo, retornando um booleano e, se bem-sucedido, o caminho para o arquivo encontrado.

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 da página de impulso mencionada acima.

Para sistemas baseados em Unix / Linux:

Você pode usar opendir / readdir / closedir .

O código de amostra que pesquisa em um diretório a entrada `` 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;

Código fonte das páginas de manual acima.

Para sistemas baseados em Windows:

Você pode usar as funções FindFirstFile / FindNextFile / FindClose da API do Win32 .

O exemplo a seguir em C ++ mostra um uso mínimo do 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 fonte das páginas msdn acima.

Brian R. Bondy
fonte
Uso:FindFirstFile(TEXT("D:\\IMAGE\\MYDIRECTORY\\*"), &findFileData);
Константин Ван
7
Com o C ++ 14, existe std::experimental::filesystem, com o C ++ 17 std::filesystem, que possuem funcionalidades semelhantes às do boost (as bibliotecas são derivadas do boost). Veja a resposta de Shreevardhan abaixo.
precisa saber é o seguinte
Para Windows, consulte docs.microsoft.com/en-us/windows/desktop/FileIO/... para mais detalhes
FindOutIslamNow
90

Uma função é suficiente, você não precisa usar nenhuma biblioteca de terceiros (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;
}

PS: como mencionado por @Sebastian, você pode mudar *.*para *.extpara obter apenas os arquivos EXT (ou seja, de um tipo específico) nesse diretório.

herohuyongtao
fonte
20
Esta solução é específica da plataforma. Esse é o motivo pelo qual você precisa de bibliotecas de terceiros.
Kraxor
9
@kraxor Sim, ele só funciona no Windows, mas o OP nunca pede para ter uma solução de plataforma cruzada. BTW, eu sempre prefiro escolher algo sem usar a 3ª biblioteca (se possível).
Herohuyongtao 29/05
7
O @herohuyongtao OP nunca especificou uma plataforma, e fornecer uma solução altamente dependente da plataforma para uma pergunta genérica pode ser enganosa. (E se houver uma solução em uma linha que funcione apenas no PlayStation 3? Essa é uma boa resposta aqui?). Vejo que você editou sua resposta para afirmar que ela só funciona no Windows, acho que é bom assim.
Kraxor
1
@herohuyongtao OP mencionou que não pode analisar ls, o que significa que ele provavelmente está no unix .. de qualquer maneira, boa resposta para o Windows.
Thomas
2
Acabei usando std::vector<std::wstring>ae, em fileName.c_str()vez de um vetor de strings, que não seria compilado.
PerryC
51

Para uma solução apenas C, verifique isso. Requer apenas um cabeçalho extra:

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

Algumas vantagens sobre outras opções:

  • É portátil - envolve o POSIX dirent e o Windows FindFirstFile
  • Ele usa readdir_rquando disponível, o que significa que é (geralmente) seguro para threads
  • Suporta Windows UTF-16 através das mesmas UNICODEmacros
  • É C90, então até compiladores muito antigos podem usá-lo
congusbongus
fonte
2
Sugestão muito boa. Eu não testei-o em um computador com Windows ainda, mas funciona brilhantemente no OS X.
ArtOfWarfare
A biblioteca não suporta std :: string, portanto você não pode passar file.c_str () para tinydir_open. Emite o erro C2664 durante a compilação no msvc 2015 neste caso.
Stepan Yakovenko
33

Eu recomendo usar globcom este invólucro reutilizável. Ele gera um vector<string>caminho correspondente ao arquivo que se ajusta ao padrão 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;
}

Que pode ser chamado com um padrão curinga normal do sistema, como:

vector<string> files = globVector("./*");
Chris Redford
fonte
2
Teste se glob () retorna zero.
Camille Goudeseune
Eu gostaria de usar glob.h como você recomendou. Ainda assim, não posso incluir o arquivo .h: está escrito No such file or directory. Você pode me dizer como resolver esse problema, por favor?
23616 Tofuw
Observe que essa rotina atinge apenas um nível de profundidade (sem recursão). Ele também não fazer uma verificação rápida para determinar se se trata de um arquivo ou pasta, o que você pode fazer facilmente, alternando GLOB_TILDEcom GLOB_TILDE | GLOB_MARKe, em seguida, verificar se há caminhos que terminam em uma barra. Você precisará fazer uma modificação, se precisar.
Volomike 15/05
Esta plataforma é compatível?
Nikhil Augustine
Infelizmente você não pode encontrar arquivos ocultos uniformemente via glob.
LmTinyToon 5/10
23

Aqui está um código muito simples no C++11uso da boost::filesystembiblioteca para obter nomes de arquivos em um diretório (excluindo nomes de pastas):

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

A saída é como:

file1.txt
file2.dat
Ruim
fonte
Olá, e onde posso obter esta biblioteca?
Alexander Leon VI
2
@Alexander De Leon: Você pode obter esta biblioteca em seu site boost.org , leia guia de iniciação em primeiro lugar, em seguida, usar a sua boost::filesystembiblioteca boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm
Bad
23

Por que não 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
fonte
Essa poderia ser uma resposta melhor se você explicar as inclusões necessárias.
Volomike 15/05
2
Teste se glob () retorna zero!
Orbitcowboy 20/05
Isso é bom quando você conhece o arquivo que você está procurando, como * .txt
Kemin Zhou
20

Eu acho que, abaixo do snippet, pode ser usado para listar todos os arquivos.

#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 seguir está a estrutura do diretório dir

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
fonte
Eu gostaria deste.
precisa saber é
10

Experimente o boost para o método x-platform

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

ou apenas use o arquivo específico do seu sistema operacional.

Tim
fonte
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada. - Da avaliação
ice1000 28/06
@ ice1000 Sério? Esta Q&A é de 2009
Tim
8

Confira esta classe que usa a API do win32. Apenas construa uma instância fornecendo a foldernamepartir da qual você deseja a listagem e chame o getNextFilemétodo para obter a próxima a filenamepartir do diretório Eu acho que precisa 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;
    }

};
robertvarga
fonte
6

GNU Manual FTW

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

Além disso, às vezes é bom ir direto à fonte (trocadilho intencional). Você pode aprender muito observando as entranhas de alguns dos comandos mais comuns no Linux. Eu configurei um espelho simples dos coreutils do GNU no github (para leitura).

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

Talvez isso não atenda ao Windows, mas vários casos de uso de variantes do Unix podem ser encontrados usando esses métodos.

Espero que ajude...

Homer6
fonte
5

Shreevardhan resposta funciona muito bem. Mas se você quiser usá-lo no c ++ 14, faça uma alteraçãonamespace fs = experimental::filesystem;

ou seja,

#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
fonte
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
fonte
3

Espero que este código o ajude.

#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
fonte
4
-1. string2wchar_tretorna o endereço de uma variável local. Além disso, você provavelmente deve usar os métodos de conversão disponíveis no WinAPI em vez de escrever seus próprios.
Daniel Kamil Kozar
3

Essa implementação realiza seu objetivo, preenchendo dinamicamente uma matriz de seqüências de caracteres com o conteúdo do diretório 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
fonte
Como eu chamaria isso? Estou recebendo segfaults quando tento executar esta função no primeiro ifbloco. Eu estou chamando-lo comchar **list; int numItems; exploreDirectory("/folder",list, numItems);
Hal T
2

Isso funciona para mim. Me desculpe se não consigo me lembrar da fonte. Provavelmente é de uma 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
fonte
2

você pode obter todos os arquivos diretos em seu diretório raiz usando std :: experimental :: filesystem :: directory_iterator (). Em seguida, leia o nome desses arquivos de caminho.

#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
fonte
2

Esta resposta deve funcionar para usuários do Windows que tiveram problemas ao trabalhar com o Visual Studio com qualquer uma das outras respostas.

  1. Faça o download do arquivo dirent.h na página do github. Mas é melhor usar apenas o arquivo Raw dirent.h e seguir meus passos abaixo (é como eu o fiz funcionar).

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

    Arquivo Direto não processado: Arquivo dirent.h não processado

  2. Vá para o seu projeto e adicione um novo item ( Ctrl+ Shift+ A). Adicione um arquivo de cabeçalho (.h) e chame-o de dirent.h.

  3. Cole o código do arquivo dirent.h bruto no seu cabeçalho.

  4. Inclua "dirent.h" no seu código.

  5. Coloque o void filefinder()método abaixo no seu código e chame-o da sua mainfunção ou edite a função como deseja usá-lo.

    #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
fonte
1

Sistema chamá-lo!

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

Então basta ler o arquivo.

EDIT: Essa resposta deve ser considerada um hack, mas realmente funciona (embora de uma maneira específica da plataforma) se você não tiver acesso a soluções mais elegantes.

Catalisador
fonte
7
Não tenho permissão para executar o comando 'ls' e analisar os resultados de dentro do meu programa. Eu sabia que haveria alguém que iria enviar algo assim ...
yyny
1

Como os arquivos e subdiretórios de um diretório geralmente são armazenados em uma estrutura em árvore, uma maneira intuitiva é usar o algoritmo DFS para percorrer recursivamente cada um deles. Aqui está um exemplo no sistema operacional Windows, usando funções básicas de arquivo em io.h. Você pode substituir essas funções em outra plataforma. O que quero expressar é que a idéia básica do DFS atende perfeitamente a esse 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
fonte
1

Tentei seguir o exemplo dado nas duas respostas e pode valer a pena notar que parece que std::filesystem::directory_entryfoi alterado para não ter uma sobrecarga do <<operador. Em vez de std::cout << p << std::endl;, tive que usar o seguinte para poder compilar e fazê-lo funcionar:

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

tentar passar ppor si próprio para std::cout <<resultar em um erro de sobrecarga ausente.

StarKiller4011
fonte
1

Com base no que herohuyongtao postou e em algumas outras postagens:

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

Qual é o tipo de entrada esperado de FindFirstFile?

Como converter wstring em string?

Esta é uma solução do Windows.

Como eu queria passar em std :: string e retornar um vetor de strings, tive que fazer algumas conversões.

#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
fonte
Se você souber que usará apenas multibyte, poderá usar WIN32_FIND_DATAA, FindFirstFileAe FindNextFileA. Então não haverá necessidade de converter resultado em multibyte ou Entrada em unicode.
Kiran Thilak
0

Apenas algo que eu quero compartilhar e obrigado pelo material de leitura. Brinque um pouco com a função para entendê-la. Você pode gostar. e significava extensão, p é para caminho e s é para separador de caminho.

Se o caminho for passado sem terminar o separador, um separador será anexado ao caminho. Para a extensão, se uma string vazia for inserida, a função retornará qualquer arquivo que não tenha uma extensão em seu nome. Se uma única estrela foi inserida, todos os arquivos no diretório serão retornados. Se e comprimento for maior que 0, mas não for um único *, um ponto será anexado a e se e não contiver um ponto na posição zero.

Para um valor retornado. Se um mapa de tamanho zero for retornado, nada foi encontrado, mas o diretório foi aberto corretamente. Se o índice 999 estiver disponível no valor de retorno, mas o tamanho do mapa for apenas 1, isso significava que havia um problema ao abrir o caminho do diretório.

Observe que, para eficiência, esta função pode ser dividida em três funções menores. Além disso, você pode criar uma função de chamada que detectará qual função será chamada com base na entrada. Por que isso é mais eficiente? Disse que se você vai pegar tudo o que é um arquivo, nesse método, a subfunção criada para pegar todos os arquivos irá apenas pegar todos os arquivos e não precisará avaliar nenhuma outra condição desnecessária toda vez que encontrar um arquivo.

Isso também se aplica a quando você pega arquivos que não têm extensão. Uma função construída específica para esse fim avaliaria apenas o clima se o objeto encontrado for um arquivo e, em seguida, se o nome do arquivo possui ou não um ponto.

A economia pode não ser muito se você ler apenas diretórios com pouco arquivo. Mas se você estiver lendo uma grande quantidade de diretório ou se o diretório tiver centenas de milhares de arquivos, pode ser uma grande economia.

#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
fonte
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
fonte
-1

Isso funcionou para mim. Ele grava um arquivo com apenas os nomes (sem caminho) de todos os arquivos. Em seguida, ele lê o arquivo txt e o imprime para você.

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
fonte