it-swarm-ko.tech

표준 C ++에서 모든 파일 / 디렉토리를 재귀 적으로 반복하는 방법은 무엇입니까?

표준 C++에서 모든 파일/디렉토리를 재귀 적으로 반복하는 방법은 무엇입니까?

102
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++ 17에서는 <filesystem> 헤더 및 range -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> 헤더 (더 이상 "실험적"이 아님).

36
Adi Shavit

새로운 C++ 11 범위 기반 forBoost 를 사용하여 훨씬 간단하게 만들 수 있습니다.

#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의 Dirent.h 라이브러리를 사용하는 것입니다.

Wikipedia의 작업 코드 단편 :

#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 :: wxDirQt :: QDir 을 검사 할 수 있습니다.

WxWidget과 Qt는 모두 오픈 소스, 크로스 플랫폼 C++ 프레임 워크입니다.

wxDirTraverse() 또는 더 간단한 GetAllFiles() 함수를 사용하여 파일을 재귀 적으로 순회하는 유연한 방법을 제공합니다. 또한 GetFirst()GetNext() 함수를 사용하여 순회를 구현할 수 있습니다 (Traverse () 및 GetAllFiles ()는 결국 GetFirst () 및 GetNext () 함수를 사용하는 래퍼라고 가정).

QDir는 디렉토리 구조와 그 내용에 대한 액세스를 제공합니다. QDir을 사용하여 디렉토리를 탐색하는 방법에는 여러 가지가 있습니다. QDirIterator :: Subdirectories 플래그로 인스턴스화 된 QDirIterator를 사용하여 디렉토리 컨텐츠 (하위 디렉토리 포함)를 반복 할 수 있습니다. 또 다른 방법은 QDir의 GetEntryList () 함수를 사용하고 재귀 순회를 구현하는 것입니다.

다음은 모든 하위 디렉토리를 반복하는 방법을 보여주는 샘플 코드 ( here # 예제 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) 을 사용하여 [~ # ~] posix [~ # ~] 시스템.

4
leif

부스트 또는 c ++ 14의 실험적인 파일 시스템을 사용하는 것이 가장 좋습니다. [~ # ~] if [~ # ~] 내부 디렉토리를 구문 분석하고 있습니다 (예 : 프로그램이 닫힌 후 데이터를 저장하기 위해 프로그램에 사용) )을 입력 한 다음 파일 내용의 색인이있는 색인 파일을 만듭니다. 그건 그렇고, 나중에 부스트를 사용해야 할 수도 있으므로 설치하지 않은 경우 설치하십시오! 둘째, 다음과 같은 조건부 컴파일을 사용할 수 있습니다.

#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.
4
ndrewxie

당신은하지 않습니다. C++ 표준에는 디렉토리 개념이 없습니다. 문자열을 파일 핸들로 바꾸는 것은 구현에 달려 있습니다. 해당 문자열의 내용과 매핑되는 내용은 OS에 따라 다릅니다. C++을 사용하여 해당 OS를 작성할 수 있으므로 디렉토리를 반복하는 방법을 묻는 방법이 아직 정의되지 않은 수준에서 사용됩니다 (디렉토리 관리 코드를 작성하기 때문에).

이를 수행하는 방법은 OS API 설명서를 참조하십시오. 이식성이 필요하다면 다양한 OS에 대해 많은 # ifdef s가 있어야합니다.

3
Matthew Scouten

open()readdir()과 같이 파일 시스템 탐색을 위해 OS 특정 함수를 호출해야합니다. C 표준은 파일 시스템 관련 기능을 지정하지 않습니다.

2
John Millikin

당신은하지 않습니다. 표준 C++는 디렉토리 개념에 노출되지 않습니다. 특히 디렉토리의 모든 파일을 나열하는 방법을 제공하지 않습니다.

끔찍한 해킹은 system () 호출을 사용하고 결과를 파싱하는 것입니다. 가장 합리적인 해결책은 Qt 또는 심지어 POSIX 와 같은 일종의 크로스 플랫폼 라이브러리를 사용하는 것입니다.

1
shoosh

우리는 2019 년에 있습니다. C++filesystem 표준 라이브러리가 있습니다. Filesystem library는 파일 시스템 및 해당 구성 요소 (예 : 경로, 일반 파일 및 디렉토리)에서 작업을 수행하기위한 기능을 제공합니다.

이식성 문제를 고려중인 경우 이 링크 에 대한 중요한 참고 사항이 있습니다. 그것은 말한다 :

계층 적 파일 시스템에 구현에 액세스 할 수 없거나 필요한 기능을 제공하지 않으면 파일 시스템 라이브러리 기능을 사용하지 못할 수 있습니다. 기본 파일 시스템에서 지원하지 않는 일부 기능은 사용할 수 없습니다 (예 : FAT 파일 시스템에는 심볼릭 링크가없고 여러 개의 하드 링크가 금지됨). 이러한 경우 오류를보고해야합니다.

파일 시스템 라이브러리는 원래 boost.filesystem로 개발되었으며 기술 사양 ISO/IEC TS 18822 : 2015로 게시되었으며 C++ 17에서 ISO C++로 병합되었습니다. 부스트 구현은 현재 C++ 17 라이브러리보다 더 많은 컴파일러와 플랫폼에서 사용할 수 있습니다.

@ adi-shavit 님이 std :: experimental의 일부인 경우이 질문에 답변하고 2017 년에이 답변을 업데이트했습니다. 라이브러리에 대한 자세한 내용을 제공하고보다 자세한 예를 보여 드리고 싶습니다.

std :: filesystem :: recursive_directory_iterator 는 디렉토리의 directory_entry 요소를 반복하고 모든 서브 디렉토리의 항목을 반복적으로 나타내는 LegacyInputIterator입니다. 각 디렉토리 항목이 한 번만 방문된다는 점을 제외하고 반복 순서는 지정되지 않습니다.

하위 디렉토리의 항목을 반복적으로 반복하지 않으려면 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-벽

#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