Pergunta

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 ls comando e analise os resultados de dentro do meu programa.

Foi útil?

Solução

Em tarefas pequenas e simples, não uso o impulso, uso diretor.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 modelos, como Boost (sem ofensa, eu gosto do Boost!).

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

Atualização 2017:

No C ++ 17, agora existe uma maneira oficial de listar arquivos do 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;
}

Outras dicas

C ++ 17 agora tem 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;
}

Também, std::filesystem::recursive_directory_iterator Também pode iterar os subdiretos.

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 cruzada, a melhor maneira de plataforma cruzada é usar uma biblioteca como o Módulo de sistema de arquivos Boost.

Método Cross Platform Boost:

A função a seguir, dado um caminho de diretório e um nome de arquivo, pesquisa recursivamente o diretório e seus subdiretos pelo nome do arquivo, retornando um bool e, se for 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 Boost mencionada acima.

Para sistemas baseados em UNIX/Linux:

Você pode usar OPENDIR / readdir / fechado.

Código de exemplo que pesquisa um diretório para entrada `` name '' é:

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 do homem acima.

Para sistemas baseados no Windows:

Você pode usar a API Win32 FindfirstFile / FindNextFile / FindClose funções.

O exemplo C ++ a seguir 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.

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 *.ext Para obter apenas os arquivos ext (ou seja, de um tipo específico) nesse diretório.

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 Posix Dirent e Windows FindfirstFile
  • Ele usa readdir_r Onde disponível, o que significa que é (geralmente) ThreadSafe
  • Suporta o Windows UTF-16 através do mesmo UNICODE macros
  • É C90, então mesmo compiladores muito antigos podem usá -lo

Eu recomendo usar glob Com este invólucro reutilizável. Gera um vector<string> correspondente a caminhos de arquivo que se encaixam no 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 então ser chamado com um padrão de curinga do sistema normal, como:

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

Aqui está um código muito simples em C++11 usando boost::filesystem Biblioteca 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

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

Eu acho que abaixo o 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 da estrutura

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

Tente aumentar o método x-plataform

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

Ou apenas use seu material de arquivo específico do sistema operacional.

Confira esta classe que usa a API Win32. Basta construir uma instância, fornecendo o foldername do qual você deseja a listagem, então ligue para o getNextFile Método para obter o próximo filename do diretório. Eu acho que precisa windows.h e 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;
    }

};

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 para a fonte (trocadilhos). Você pode aprender muito olhando para as entranhas de alguns dos comandos mais comuns do Linux. Eu configurei um espelho simples dos coreutils da GNU no Github (para leitura).

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

Talvez isso não resolva o Windows, mas vários casos de uso do UNIX variantes podem ser obtidos usando esses métodos.

Espero que ajude...

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

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

A resposta de Shreevardhan funciona muito bem. Mas se você quiser usá -lo em C ++ 14, faça uma mudança namespace 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;
}

Essa implementação cumpre seu objetivo, preenchendo dinamicamente uma matriz de strings 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;
}

Isso funciona para mim. Sinto muito se não me lembro da fonte. Provavelmente é de uma página de homem.

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

Você pode obter todos os arquivos diretos no seu diretório raiz usando o 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;
}

Sistema Chame!

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

Em seguida, basta ler o arquivo.

EDIT: Esta 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.

Como os arquivos e sub -diretórios de um diretório geralmente são armazenados em uma estrutura de árvore, uma maneira intuitiva é usar o algoritmo DFS para atravessar recursivamente cada um deles. Aqui está um exemplo no sistema operacional Windows usando funções básicas de arquivo no io.h. Você pode substituir essas funções em outra plataforma. O que eu 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);
}

Essa resposta deve funcionar para os usuários do Windows que tiveram problemas para fazer isso funcionar 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 o arquivo Raw Dirent.H e seguir minhas etapas abaixo (foi como eu o fiz funcionar).

    Página do Github para Dirent.H for Windows: Página do Github para Dirent.h

    Arquivo Raw Dirent: Arquivo Raw Dirent.h

  2. Vá para o seu projeto e adicione um novo item (Ctrl+Mudança+UMA). Adicione um arquivo de cabeçalho (.h) e nomeie -o diretor.h.

  3. Cole o Arquivo Raw Dirent.h Code em seu cabeçalho.

  4. Inclua "Dirent.h" em seu código.

  5. Coloque o abaixo void filefinder() método em seu código e chame -o de seu main função ou edite a função como você deseja usá -la.

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

Eu tentei seguir o exemplo dado em Ambas respostas E pode valer a pena notar que parece que std::filesystem::directory_entry foi alterado para não ter uma sobrecarga do << operador. Ao invés de std::cout << p << std::endl; Eu 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;
}

tentando passar p por si só para std::cout << resultou em um erro de sobrecarga ausente.

Apenas algo que quero compartilhar e agradecer pelo material de leitura. Brinque com a função um pouco para entendê -la. Você pode gostar. E representou a extensão, P é para o caminho e S é para separador de caminho.

Se o caminho for passado sem acabar com 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 o comprimento E for maior que 0, mas não for um único *, um ponto será preso a e se E não tivesse contido um ponto na posição zero.

Para um valor de retorno. Se um mapa de comprimento zero for devolvido, nada foi encontrado, mas o diretório estava aberto. Se o índice 999 estiver disponível no valor de retorno, mas o tamanho do mapa é apenas 1, isso significava que havia um problema na abertura do caminho do diretório.

Observe que, para a eficiência, essa função pode ser dividida em 3 funções menores. Além disso, você pode criar uma função de chamador que detectará qual função ela chamará com base na entrada. Por que isso é mais eficiente? Disse que você for pegar tudo o que é um arquivo, fazendo o método, a subfunção que criou para agarrar todos os arquivos apenas agarrará todos os arquivos e não precisa avaliar nenhuma outra condição desnecessária toda vez que encontrar um arquivo.

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

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

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

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

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

    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top