Как получить список файлов в каталоге с помощью C или c++?

Как я могу определить список файлов в каталоге из моего кода c/" class="blnk">C или C++?

мне не разрешено выполнять ls команда и анализ результатов из моей программы.

24 ответов


в небольших и простых задачах я не использую boost, я использую dirent.h, который также доступен для 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;
}

Это просто небольшой файл заголовка и делает большинство простых вещей, которые вам нужны, не используя большой подход на основе шаблонов, как boost(не обижайтесь, мне нравится boost!).

автором уровня совместимости windows является Тони Ронкко. В Unix, это стандартный заголовок.

обновление 2017:

В C++17 есть теперь это официальный способ перечислить файлы вашей файловой системы:std::filesystem. Есть отличный ответ от Shreevardhan ниже с этим исходным кодом:

#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main()
{
    std::string path = "/path/to/directory";
    for (auto & p : fs::directory_iterator(path))
        std::cout << p << std::endl;
}

рассмотрите возможность повышения его ответа, если вы используете подход C++17.


к сожалению, стандарт C++ не определяет стандартный способ работы с файлами и папками в эту сторону.

поскольку нет кросс-платформенного способа, лучший кросс-платформенный способ-использовать библиотеку, такую как модуль файловой системы boost.

кросс-платформенный метод повышения:

следующая функция, учитывая путь к каталогу и имя файла, рекурсивно ищет каталог и его подкаталоги для имя файла, возврат bool, и в случае успеха, путь к файлу, который был найден.

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

источник со страницы boost, упомянутой выше.


для систем на базе Unix / Linux:

можно использовать opendir / вызове readdir / closedir.

пример кода, который ищет каталог для записи "имя":

   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;

исходный код из вышеуказанных страниц.


для систем на базе windows:

вы можете использовать Win32 API FindFirstFile / findnextfile функция / FindClose функции.

в следующем примере C++ показано минимальное использование 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);
   }
}

исходный код с вышеуказанных страниц msdn.


C++17 теперь имеет std::filesystem::directory_iterator, который можно использовать как

#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main()
{
    std::string path = "/path/to/directory";
    for (const auto & p : fs::directory_iterator(path))
        std::cout << p << std::endl; // "p" is the directory entry. Get the path with "p.path()".
}

и std::filesystem::recursive_directory_iterator итерироваться и подкаталоги.


достаточно одной функции, вам не нужно использовать стороннюю библиотеку (для 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: Как упоминалось @Sebastian, вы можете изменить *.* to *.ext чтобы получить только EXT-файлы (т. е. определенного типа) в этом каталоге.


для решения только C, пожалуйста, проверьте это. Для этого требуется только дополнительный заголовок:

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

некоторые преимущества перед другими вариантами:

  • это портативный-обертывания POSIX dirent и Windows FindFirstFile
  • он использует readdir_r где доступно, что означает, что это (обычно) threadsafe
  • поддерживает Windows UTF-16 через то же самое UNICODE макрос
  • это C90 так что даже очень древние компиляторы могут использовать его

Я рекомендую использовать glob С этой оболочкой многоразовые. Он генерирует vector<string> соответствующие пути к файлам, которые соответствуют шаблону 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;
}

что тогда можно назвать нормальным шаблону системы, такие как:

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

вот очень простой код C++11 используя boost::filesystem библиотека для получения имен файлов в каталоге (за исключением имен папок):

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

выход такой:

file1.txt
file2.dat

почему бы не использовать 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;
}

Я думаю, ниже фрагмента можно использовать для списка всех файлов.

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

Ниже приведена структура структуры 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 */
};

попробуйте boost для метода x-platform

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

или просто используйте свой конкретный файл для ОС.


проверьте этот класс, который использует win32 api. Просто создайте экземпляр, предоставив foldername из которого вы хотите список, то вызов getNextFile метод, чтобы получить следующий filename из каталога. Я думаю, что это нужно windows.h и 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 FTW

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

кроме того, иногда это хорошо, чтобы пойти прямо к источнику (каламбур). Вы можете многому научиться, глядя на внутренности некоторых наиболее часто используемые команды в Linux. Я установил простое зеркало coreutils GNU на github (для чтение.)

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

возможно, это не относится к Windows, но ряд случаев использования вариантов Unix может быть с помощью этих методов.

надеюсь, это поможет...


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

Я надеюсь, что этот код поможет вам.

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

эта реализация реализует вашу цель, динамически заполняя массив строк содержимым указанного каталога.

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

это работает для меня. Простите, если не могу вспомнить источник. Вероятно, это с мужской страницы.

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

Shreevardhan ответ работает отлично. Но если вы хотите использовать его в C++14 просто внести изменения namespace fs = experimental::filesystem;

то есть,

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

вы можете получить все прямые файлы в корневом каталоге с помощью std:: experimental::filesystem:: directory_iterator(). Затем прочитайте название этих pathfiles.

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

системный вызов!

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

тогда просто прочитайте файл.

EDIT: этот ответ следует рассматривать как взлом, но он действительно работает (хотя и определенным образом), если у вас нет доступа к более элегантным решениям.


этот ответ должен работать для пользователей Windows, у которых возникли проблемы с этой работой с Visual Studio с любым другим ответом.

  1. скачать dirent.H файл со страницы github. Но лучше просто использовать Raw dirent.H файл и выполните мои шаги ниже (это то, как я получил его на работу).

    страница Github для dirent.h для Windows:страница Github для dirent.h

    Raw Dirent File:Raw dirent.ч Файл

  2. перейдите в свой проект и добавьте новый элемент ( Ctrl+ Shift+A). Добавить файл заголовка (.h) и назовите его dirent.h.

  3. вставить Raw dirent.файл H код в заголовок.

  4. включить " dirent.h " в вашем коде.

  5. поставить ниже void filefinder() метод в вашем коде и вызовите его из вашего main функция или редактировать функции, как вы хотите использовать его.

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

поскольку файлы и подкаталоги каталога обычно хранятся в древовидной структуре, интуитивно понятным способом является использование алгоритма DFS для рекурсивного обхода каждого из них. Вот пример в операционной системе windows с помощью основных файловых функций в io.h. Вы можете заменить эти функции в другой платформе. Что я хочу выразить, так это то, что основная идея DFS идеально подходит для этой проблемы.

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

Я попытался следовать примеру, приведенному в и ответы и, возможно, стоит отметить, что это выглядит так, как будто std::filesystem::directory_entry было изменено, чтобы не было перегрузки << оператора. Вместо std::cout << p << std::endl; мне пришлось использовать следующее, чтобы скомпилировать и заставить его работать:

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

пыталась передать p на std::cout << вылилось в ошибку по перегрузке.


просто то, что я хочу поделиться и поблагодарить вас за чтение материала. Поиграйте с функцией немного, чтобы понять ее. Тебе может понравиться. e означало расширение, p-путь, а s-разделитель пути.

Если путь передается без конечного разделителя, разделитель будет добавлен к пути. Для расширения, если введена пустая строка, функция вернет любой файл, который не имеет расширения в своем имени. Если бы одна звезда была вводится, чем все файлы в каталоге будут возвращены. Если длина e больше 0, но не является единицей*, то точка будет добавлена к e, если e не содержит точку в нулевом положении.

для возвращаемого значения. Если возвращается карта нулевой длины, то ничего не найдено, но каталог был открыт нормально. Если индекс 999 доступен из возвращаемого значения, но размер карты равен только 1, это означает, что возникла проблема с открытием пути к каталогу.

обратите внимание, что для эффективности, эту функцию можно разделить в 3 более небольших функции. Кроме того, вы можете создать функцию вызывающего абонента, которая определит, какую функцию он будет вызывать на основе ввода. Почему это более эффективно? Сказал, что если вы собираетесь захватить все, что является файлом, делая этот метод, подфункция, построенная для захвата всех файлов, просто захватит все, что является файлами, и не нужно оценивать любые другие ненужные условия каждый раз, когда он нашел файл.

Это было бы также применяются, когда вы захватить файлы, которые не имеют расширения. Определенная встроенная функция для этой цели будет оценивать только погоду, если найденный объект является файлом, а затем, имеет ли имя файла точку в нем.

сохранение может быть не так много, если Вы читаете только каталоги с не так много файлов. Но если Вы читаете массовое количество каталогов или если каталог имеет пару сотен тысяч файлов, это может быть огромная экономия.

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

это сработало для меня. Он записывает файл только с именами (без пути) всех файлов. Затем он читает текстовый файл и распечатывает его для вас.

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

    }