Как я могу получить список файлов в каталоге, используя C или C ++? - PullRequest
521 голосов
/ 04 марта 2009

Как определить список файлов в каталоге из моего кода C или C ++?

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

Ответы [ 24 ]

720 голосов
/ 04 марта 2009

В небольших и простых задачах я не использую 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!).

Автор слоя совместимости окон - Тони Ронкко. В 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 (const auto & entry : fs::directory_iterator(path))
        std::cout << entry.path() << std::endl;
}
255 голосов
/ 28 мая 2016

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 & entry : fs::directory_iterator(path))
        std::cout << entry.path() << std::endl;
}

Кроме того, std::filesystem::recursive_directory_iterator также может выполнять итерации подкаталогов.

221 голосов
/ 04 марта 2009

К сожалению, стандарт 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;
}

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

Для систем на основе Unix / Linux:

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

Пример кода, который ищет в каталоге запись `` 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;

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

Для систем на базе 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.

79 голосов
/ 31 декабря 2013

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

47 голосов
/ 04 февраля 2013

Для решения только на 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 там, где доступно, что означает, что он (как правило) безопасен для потоков
  • Поддерживает Windows UTF-16 через те же макросы UNICODE
  • Это C90, поэтому его могут использовать даже очень древние компиляторы
29 голосов
/ 11 июля 2014

Я рекомендую использовать 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("./*");
21 голосов
/ 25 июня 2015

Вот очень простой код в 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
18 голосов
/ 10 февраля 2014

Почему бы не использовать 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;
}
17 голосов
/ 23 октября 2013

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

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

Ниже приведена структура struct 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 */
};
10 голосов
/ 04 марта 2009

Попробуйте повысить для метода x-платформы

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...