it-swarm-ru.tech

Как вы рекурсивно просматриваете каждый файл / каталог в стандартном C ++?

Как вы рекурсивно просматриваете каждый файл/каталог в стандартном C++?

100
robottobor

В стандарте C++ это технически невозможно, поскольку в стандарте C++ отсутствует концепция каталогов. Если вы хотите немного расширить свою сеть, вы можете посмотреть на использование Boost.FileSystem . Это было принято для включения в TR2, так что это дает вам лучший шанс сохранить вашу реализацию как можно ближе к стандарту.

Пример, взятый прямо с сайта:

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;
}
94
1800 INFORMATION

При использовании Win32 API вы можете использовать функции FindFirstFile и FindNextFile .

http://msdn.Microsoft.com/en-us/library/aa365200 (VS.85) .aspx

Для рекурсивного обхода каталогов вы должны проверить каждый WIN32_FIND_DATA.dwFileAttributes , чтобы проверить, если FILE_ATTRIBUTE_DIRECTORY бит установлен. Если бит установлен, то вы можете рекурсивно вызвать функцию с этим каталогом. В качестве альтернативы вы можете использовать стек для обеспечения того же эффекта рекурсивного вызова, но избежать переполнения стека для очень длинных деревьев пути.

#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>

using namespace std;

bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATA ffd;
    wstring spec;
    stack<wstring> directories;

    directories.Push(path);
    files.clear();

    while (!directories.empty()) {
        path = directories.top();
        spec = path + L"\\" + mask;
        directories.pop();

        hFind = FindFirstFile(spec.c_str(), &ffd);
        if (hFind == INVALID_HANDLE_VALUE)  {
            return false;
        } 

        do {
            if (wcscmp(ffd.cFileName, L".") != 0 && 
                wcscmp(ffd.cFileName, L"..") != 0) {
                if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    directories.Push(path + L"\\" + ffd.cFileName);
                }
                else {
                    files.Push_back(path + L"\\" + ffd.cFileName);
                }
            }
        } while (FindNextFile(hFind, &ffd) != 0);

        if (GetLastError() != ERROR_NO_MORE_FILES) {
            FindClose(hFind);
            return false;
        }

        FindClose(hFind);
        hFind = INVALID_HANDLE_VALUE;
    }

    return true;
}

int main(int argc, char* argv[])
{
    vector<wstring> files;

    if (ListFiles(L"F:\\cvsrepos", L"*", files)) {
        for (vector<wstring>::iterator it = files.begin(); 
             it != files.end(); 
             ++it) {
            wcout << it->c_str() << endl;
        }
    }
    return 0;
}
42
Jorge Ferreira

Вы можете сделать это еще проще с новыми C++ 11 диапазон на основе for и Boost :

#include <boost/filesystem.hpp>

using namespace boost::filesystem;    
struct recursive_directory_range
{
    typedef recursive_directory_iterator iterator;
    recursive_directory_range(path p) : p_(p) {}

    iterator begin() { return recursive_directory_iterator(p_); }
    iterator end() { return recursive_directory_iterator(); }

    path p_;
};

for (auto it : recursive_directory_range(dir_path))
{
    std::cout << it << std::endl;
}
31
Matthieu G

С C++ 17, заголовок <filesystem> и диапазон -for, вы можете просто сделать это:

#include <filesystem>

using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
...
for (const auto& dirEntry : recursive_directory_iterator(myPath))
     std::cout << dirEntry << std::endl;

Начиная с C++ 17, std::filesystem является частью стандартной библиотеки и может быть найден в заголовке <filesystem> (больше не "экспериментальный").

30
Adi Shavit

Быстрое решение - использование библиотеки C Dirent.h .

Фрагмент рабочего кода из Википедии:

#include <stdio.h>
#include <dirent.h>

int listdir(const char *path) {
    struct dirent *entry;
    DIR *dp;

    dp = opendir(path);
    if (dp == NULL) {
        perror("opendir: Path does not exist or could not be read.");
        return -1;
    }

    while ((entry = readdir(dp)))
        puts(entry->d_name);

    closedir(dp);
    return 0;
}
23
Alex

В дополнение к вышеупомянутой boost :: filesystem вы можете изучить wxWidgets :: wxDir и Qt :: QDir .

И wxWidgets, и Qt являются кроссплатформенными средами C++ с открытым исходным кодом.

wxDir предоставляет гибкий способ рекурсивного обхода файлов с использованием Traverse() или более простой функции GetAllFiles(). Также вы можете реализовать обход с помощью функций GetFirst() и GetNext() (я предполагаю, что Traverse () и GetAllFiles () являются оболочками, которые в конечном итоге используют функции GetFirst () и GetNext ()).

QDir обеспечивает доступ к структурам каталогов и их содержимому. Есть несколько способов просмотреть каталоги с помощью QDir. Вы можете перебирать содержимое каталога (включая подкаталоги) с помощью QDirIterator, для которого был создан флаг QDirIterator :: Subdirectories. Другой способ - использовать функцию QDir GetEntryList () и реализовать рекурсивный обход.

Вот пример кода (взят из здесь # Пример 8-5), который показывает, как перебирать все подкаталоги.

#include <qapplication.h>
#include <qdir.h>
#include <iostream>

int main( int argc, char **argv )
{
    QApplication a( argc, argv );
    QDir currentDir = QDir::current();

    currentDir.setFilter( QDir::Dirs );
    QStringList entries = currentDir.entryList();
    for( QStringList::ConstIterator entry=entries.begin(); entry!=entries.end(); ++entry) 
    {
         std::cout << *entry << std::endl;
    }
    return 0;
}
10
mrvincenzo

Boost :: filesystem предоставляет recursive_directory_iterator, что весьма удобно для этой задачи:

#include "boost/filesystem.hpp"
#include <iostream>

using namespace boost::filesystem;

recursive_directory_iterator end;
for (recursive_directory_iterator it("./"); it != end; ++it) {
    std::cout << *it << std::endl;                                    
}
6
DikobrAz

Вы можете использовать ftw(3) ИЛИ nftw(3) для обхода иерархии файловой системы в C или C++ в POSIX системах.

4
leif

Вы не Стандарт C++ не имеет понятия о каталогах. Это зависит от реализации, чтобы превратить строку в дескриптор файла. Содержимое этой строки и то, на что она отображается, зависит от ОС. Имейте в виду, что C++ может использоваться для написания этой ОС, поэтому он используется на уровне, когда вопрос о том, как перебирать каталог, еще не определен (потому что вы пишете код управления каталогом).

Посмотрите документацию по вашей ОС API, чтобы узнать, как это сделать. Если вам нужно быть переносимым, вам понадобится набор # ifdef для различных ОС.

3
Matthew Scouten

Вы, вероятно, лучше всего подойдете для файловой системы boost или c ++ 14. ЕСЛИ вы анализируете внутренний каталог (т.е. используемый для вашей программы для хранения данных после закрытия программы), затем создайте индексный файл, который имеет индекс содержимого файла. Кстати, вам, вероятно, понадобится использовать boost в будущем, поэтому, если он не установлен, установите его! Во-вторых, вы можете использовать условную компиляцию, например:

#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif

Код для каждого случая взят из https://stackoverflow.com/a/67336/7077165

#ifdef POSIX //unix, linux, etc.
#include <stdio.h>
#include <dirent.h>

int listdir(const char *path) {
    struct dirent *entry;
    DIR *dp;

    dp = opendir(path);
    if (dp == NULL) {
        perror("opendir: Path does not exist or could not be read.");
        return -1;
    }

    while ((entry = readdir(dp)))
        puts(entry->d_name);

    closedir(dp);
    return 0;
}
#endif
#ifdef WINDOWS
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>

using namespace std;

bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
    HANDLE hFind = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATA ffd;
    wstring spec;
    stack<wstring> directories;

    directories.Push(path);
    files.clear();

    while (!directories.empty()) {
        path = directories.top();
        spec = path + L"\\" + mask;
        directories.pop();

        hFind = FindFirstFile(spec.c_str(), &ffd);
        if (hFind == INVALID_HANDLE_VALUE)  {
            return false;
        } 

        do {
            if (wcscmp(ffd.cFileName, L".") != 0 && 
                wcscmp(ffd.cFileName, L"..") != 0) {
                if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    directories.Push(path + L"\\" + ffd.cFileName);
                }
                else {
                    files.Push_back(path + L"\\" + ffd.cFileName);
                }
            }
        } while (FindNextFile(hFind, &ffd) != 0);

        if (GetLastError() != ERROR_NO_MORE_FILES) {
            FindClose(hFind);
            return false;
        }

        FindClose(hFind);
        hFind = INVALID_HANDLE_VALUE;
    }

    return true;
}
#endif
//so on and so forth.
3
ndrewxie

Для обхода файловой системы необходимо вызывать специфичные для ОС функции, такие как open() и readdir(). Стандарт C не определяет никаких функций, связанных с файловой системой.

2
John Millikin

Вы не Стандарт C++ не раскрывает понятие каталога. В частности, это не дает никакого способа перечислить все файлы в каталоге.

Ужасным было бы использовать системные вызовы и анализировать результаты. Наиболее разумным решением было бы использовать какую-то кроссплатформенную библиотеку, такую ​​как Qt или даже POSIX .

1
shoosh

Мы находимся в 2019 году. У нас есть файловая система стандартная библиотека в C++. Filesystem library предоставляет средства для выполнения операций с файловыми системами и их компонентами, такими как пути, обычные файлы и каталоги.

Важное замечание о эта ссылка , если вы рассматриваете проблемы переносимости. Это говорит:

Средства библиотеки файловой системы могут быть недоступны, если иерархическая файловая система недоступна для реализации или если она не обеспечивает необходимые возможности. Некоторые функции могут быть недоступны, если они не поддерживаются базовой файловой системой (например, файловая система FAT не имеет символических ссылок и запрещает множественные жесткие ссылки). В этих случаях необходимо сообщать об ошибках.

Библиотека файловой системы изначально была разработана как boost.filesystem, была опубликована в виде технической спецификации ISO/IEC TS 18822: 2015 и, наконец, объединена с ISO C++ с C++ 17. Реализация boost в настоящее время доступна на большем количестве компиляторов и платформ, чем библиотека C++ 17.

@ adi-shavit ответил на этот вопрос, когда он был частью std :: экспериментальный, и он обновил этот ответ в 2017 году. Я хочу дать более подробную информацию о библиотеке и показать более подробный пример.

std :: filesystem :: recursive_directory_iterator - это LegacyInputIterator, который перебирает элементы directory_entry каталога и, рекурсивно, над записями всех подкаталогов. Порядок итераций не указан, за исключением того, что каждая запись каталога посещается только один раз.

Если вы не хотите рекурсивно перебирать записи подкаталогов, тогда следует использовать directory_iterator .

Оба итератора возвращают объект directory_entry . directory_entry имеет различные полезные функции-члены, такие как is_regular_file, is_directory, is_socket, is_symlink и т.д. Функция-член path() возвращает объект std: : filesystem :: path и может использоваться для получения file extension, filename, root name.

Рассмотрим пример ниже. Я использовал Ubuntu и скомпилировал его через терминал, используя

g ++ example.cpp --std = c ++ 17 -lstdc ++ fs -Wall

#include <iostream>
#include <string>
#include <filesystem>

void listFiles(std::string path)
{
    for (auto& dirEntry: std::filesystem::recursive_directory_iterator(path)) {
        if (!dirEntry.is_regular_file()) {
            std::cout << "Directory: " << dirEntry.path() << std::endl;
            continue;
        }
        std::filesystem::path file = dirEntry.path();
        std::cout << "Filename: " << file.filename() << " extension: " << file.extension() << std::endl;

    }
}

int main()
{
    listFiles("./");
    return 0;
}
0
abhiarora