Как вы рекурсивно просматриваете каждый файл / каталог в стандартном C ++? - PullRequest
99 голосов
/ 16 сентября 2008

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

Ответы [ 14 ]

93 голосов
/ 16 сентября 2008

В стандарте 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;
}
42 голосов
/ 16 сентября 2008

При использовании 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;
}
31 голосов
/ 18 октября 2013

Вы можете сделать это еще проще с новыми 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;
}
27 голосов
/ 01 октября 2015

В C ++ 11/14 с «Файловой системой TS», заголовком <experimental/filesystem> и диапазоном - for вы можете просто сделать это:

#include <experimental/filesystem>

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

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

23 голосов
/ 03 сентября 2012

Быстрое решение использует библиотеку 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;
}
10 голосов
/ 24 сентября 2009

В дополнение к вышеупомянутой 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;
}
6 голосов
/ 05 мая 2014

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;                                    
}
4 голосов
/ 14 ноября 2013

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

3 голосов
/ 17 марта 2017

Скорее всего, вам лучше подойдут экспериментальные файлы с файловой системой 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 голосов
/ 16 сентября 2008

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

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

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