표준 C ++에서 모든 파일 / 디렉토리를 재귀 적으로 어떻게 반복합니까?


115

표준 C ++에서 모든 파일 / 디렉토리를 재귀 적으로 어떻게 반복합니까?




1
이것은 곧 recursive_directory_iterator 와 함께 Filesystem TS 를 통해 표준에 포함될 것입니다
Adi Shavit

표준 C 라이브러리를 사용하는 것이 C ++ 프로그램을 'standard'로 호출하는 데 방해가되지 않으면 nftw () . 다음은 실용적인 예입니다
six-k

2
자신이 무엇을하는지 아는 사람은이를 업데이트하는 데 한 시간이 걸립니다.
Josh C

답변:


99

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

5
C ++에는 파일 개념이 없습니까? std :: fstream은 어떻습니까? 아니면 fopen?
Kevin

30
디렉토리가 아닌 파일
1800 INFORMATION

22
최신 부스트 버전과 관련된 업데이트 : 누군가이 답변을 우연히 발견하는 경우 최신 부스트에는 편의 클래스 boost :: recursive_directory_iterator가 포함되어 있으므로 명시 적 재귀 호출로 위의 루프를 작성할 필요가 없습니다. 링크 : boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/...
JasDev

5
VC ++ 11은 std :: tr2 :: sys 네임 스페이스 아래의 <filesystem> 헤더에서 거의 동일한 기능을 제공합니다.
mheyman 2013-08-30

3
이것은 좋은 대답 이었지만 이제는 <filesystem>이 표준이므로 단순히 사용하는 것이 좋습니다 (예는 다른 답변 참조).
Gathar

54

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>헤더 에서 찾을 수 있습니다 (더 이상 "실험용"이 아님).


의 사용을 피 using사용을 namespace대신.
Roi Danton 2017

2
왜 그런데? 사용하지 않는 물건을 가져 오는 것보다 더 구체적입니다.
Adi Shavit

내 편집을 검토하십시오. 누락 된 네임 스페이스 표준도 추가했습니다.
Roi Danton 2017

5
<filesystem> 은 더 이상 TS가 아닙니다. C ++ 17의 일부입니다. 그에 따라이 답변을 업데이트해야합니다.
IInspectable

Mac 사용자의 경우 최소 OSX 10.15 (Catalina)가 필요합니다.
저스틴

45

Win32 API를 사용하는 경우 FindFirstFileFindNextFile 함수를 사용할 수 있습니다 .

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

19
그걸 쓰는 데 얼마나 걸렸나요? C ++를 파이썬에 붙이고 한 줄로하는 데 시간이 덜 걸릴 것이라고 생각합니다.
Dustin Getz

2
이것은 재귀 적이 지 않은 멋진 솔루션입니다 (때로는 편리합니다!).
jm.

1
Btw, 하드 코딩 된 경로 ( "F : \\ cvsrepos") 대신 경로에 대한 명령 줄 매개 변수 argv [1]을 받아들이도록 프로그램을 약간 편집하려는 경우 main (int, char)의 서명이 변경됩니다. wmain (int, wchar_t)에 다음과 같이 : int wmain (int argc, wchar_t * argv [])
JasDev 2011 년

1
감사합니다.하지만이 기능은 Cyrilic에서 작동하지 않습니다. -б, в, г 등과 같은 Cyrilic 문자로 작동하도록 만드는 방법이 있습니까?
unresolved_external

31

새로운 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;
}

5
부스트가 필요 없습니다. OP는 특별히 표준 C ++를 요청했습니다.
Craig B

23

빠른 솔루션은 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;
}

5
이 루틴은 재귀 적이 지 않습니다.
user501138

@TimCooper, 물론 그렇지 않습니다. dirent는 posix에 따라 다릅니다.
Vorac

1
사실 그것은 않습니다 당신이 토니 Ronkko하여 Visual C ++에 대한 dirent.h를의 포트를 얻을 경우 VC ++에 대한 작업을. FOSS입니다. 나는 이것을 시도했고 작동합니다.
user1741137

10

위에서 언급 한 boost :: filesystem 외에도 wxWidgets :: wxDirQt :: QDir 을 검사 할 수 있습니다 .

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

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

QDir디렉토리 구조와 그 내용에 대한 액세스를 제공합니다. QDir을 사용하여 디렉토리를 탐색하는 방법에는 여러 가지가 있습니다. QDirIterator :: Subdirectories 플래그로 인스턴스화 된 QDirIterator를 사용하여 디렉토리 내용 (하위 디렉토리 포함)을 반복 할 수 있습니다. 또 다른 방법은 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;
}

Doxygen은 QT를 OS 호환성 계층으로 사용합니다. 핵심 도구는 디렉토리 항목 (및 다른 구성 요소) 만 GUI를 전혀 사용하지 않습니다.
deft_code

7

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

1
"그것"은 무엇입니까? 구문 오류가 없습니까? 그리고 당신은 어떻게 "끝"을 공급합니까? (= 우리가 모든 디렉토리를 파싱 한 방법은 무엇입니까?)
yO_

1
@yO_ 맞아요. recursive_directory_iterator의 기본 생성자는 "유효하지 않은"반복자를 생성합니다. dir에 대한 반복을 완료하면 "it"가 무효화되고 "end"와 동일하게됩니다
DikobrAz


4

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

이를 수행하는 방법은 OS API 문서를 참조하십시오. 이식성이 필요한 경우 다양한 OS에 대한 #ifdef 여러 개가 있어야합니다 .


4

아마도 부스트 나 C ++ 14의 실험적인 파일 시스템을 사용하는 것이 가장 좋습니다. 경우 당신이 (프로그램이 종료 된 후 데이터를 저장하는 프로그램에 사용되는 예.) 내부 디렉토리를 파싱 한 후 파일 내용의 인덱스가 인덱스 파일을 확인하십시오. 그건 그렇고, 당신은 아마 나중에 부스트를 사용해야 할 것이므로 설치하지 않았다면 설치하십시오! 두 번째로 조건부 컴파일을 사용할 수 있습니다. 예 :

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

2

당신은 파일 시스템 탐색을 위해 OS 특정 함수를 호출처럼 필요 open()하고 readdir(). C 표준은 파일 시스템 관련 기능을 지정하지 않습니다.


C ++는 어떻습니까? iostream에 그러한 기능이 있습니까?
Aaron Maenpaa

2
파일에만 해당됩니다. "디렉토리에있는 모든 파일 표시"기능은 없습니다.
1800 정보

1
@ 1800 : 디렉토리는 파일입니다.
궤도의 경쾌한 경주

2

우리는 2019 년에 우리가 가지고있는 파일 시스템 의 표준 라이브러리를 C++. 는 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_iteratorLegacyInputIterator디렉토리의 directory_entry 요소를 반복하고 모든 하위 디렉토리의 항목을 반복적으로 반복하는 것입니다. 반복 순서는 각 디렉토리 항목이 한 번만 방문된다는 점을 제외하고는 지정되지 않습니다.

하위 디렉터리의 항목을 반복적으로 반복하지 않으려면 directory_iterator를 사용해야합니다.

두 반복자는 모두 directory_entry 객체를 반환합니다 . directory_entry같은 다양한 유용한 멤버 함수가 is_regular_file, is_directory, is_socket, is_symlinkpath()멤버 함수가 다시 표시의 목적 표준 : 파일 시스템 :: 경로 와 가져 오는 데 사용할 수 file extension, filename,를 root name.

아래의 예를 고려하십시오. 나는 Ubuntu터미널을 통해 사용 하고 컴파일했습니다.

g ++ 예제 .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;
}

1

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

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


1

사용할 수 있습니다 std::filesystem::recursive_directory_iterator. 그러나 여기에는 심볼릭 (소프트) 링크가 포함됩니다. 당신이 그들을 피하려면 사용할 수 있습니다 is_symlink. 사용 예 :

size_t directorySize(const std::filesystem::path& directory)
{
    size_t size{ 0 };
    for (const auto& entry : std::filesystem::recursive_directory_iterator(directory))
    {
        if (entry.is_regular_file() && !entry.is_symlink())
        {
            size += entry.file_size();
        }
    }
    return size;
}

1
마지막으로, 실제로 이전 답변보다 낫습니다.
Seyed Mehran Siadati

0

Windows를 사용하는 경우 FindNextFile API와 함께 FindFirstFile을 사용할 수 있습니다. FindFileData.dwFileAttributes를 사용하여 지정된 경로가 파일인지 디렉토리인지 확인할 수 있습니다. 디렉토리 인 경우 알고리즘을 재귀 적으로 반복 할 수 있습니다.

여기에서는 Windows 시스템의 모든 파일을 나열하는 코드를 작성했습니다.

http://dreams-soft.com/projects/traverse-directory


0

파일 트리 워크 ftw는 경로의 전체 디렉토리 트리를 벽으로 만드는 재귀적인 방법입니다. 자세한 내용은 여기에 있습니다 .

참고 : 또는 또는 fts같은 숨겨진 파일을 건너 뛸 수도 있습니다.....bashrc

#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>

 
int list(const char *name, const struct stat *status, int type)
{
     if (type == FTW_NS)
     {
         return 0;
     }

     if (type == FTW_F)
     {
         printf("0%3o\t%s\n", status->st_mode&0777, name);
     }

     if (type == FTW_D && strcmp(".", name) != 0)
     {
         printf("0%3o\t%s/\n", status->st_mode&0777, name);
     }
     return 0;
}

int main(int argc, char *argv[])
{
     if(argc == 1)
     {
         ftw(".", list, 1);
     }
     else
     {
         ftw(argv[1], list, 1);
     }

     return 0;
}

출력은 다음과 같습니다.

0755    ./Shivaji/
0644    ./Shivaji/20200516_204454.png
0644    ./Shivaji/20200527_160408.png
0644    ./Shivaji/20200527_160352.png
0644    ./Shivaji/20200520_174754.png
0644    ./Shivaji/20200520_180103.png
0755    ./Saif/
0644    ./Saif/Snapchat-1751229005.jpg
0644    ./Saif/Snapchat-1356123194.jpg
0644    ./Saif/Snapchat-613911286.jpg
0644    ./Saif/Snapchat-107742096.jpg
0755    ./Milind/
0644    ./Milind/IMG_1828.JPG
0644    ./Milind/IMG_1839.JPG
0644    ./Milind/IMG_1825.JPG
0644    ./Milind/IMG_1831.JPG
0644    ./Milind/IMG_1840.JPG

*.jpg, *.jpeg, *.png특정 요구 사항에 대해 파일 이름을 일치 시키려면 (예 : 모든 파일 검색 ) fnmatch.

 #include <ftw.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <iostream>
 #include <fnmatch.h>

 static const char *filters[] = {
     "*.jpg", "*.jpeg", "*.png"
 };

 int list(const char *name, const struct stat *status, int type)
 {
     if (type == FTW_NS)
     {
         return 0;
     }

     if (type == FTW_F)
     {
         int i;
         for (i = 0; i < sizeof(filters) / sizeof(filters[0]); i++) {
             /* if the filename matches the filter, */
             if (fnmatch(filters[i], name, FNM_CASEFOLD) == 0) {
                 printf("0%3o\t%s\n", status->st_mode&0777, name);
                 break;
             }
         }
     }

     if (type == FTW_D && strcmp(".", name) != 0)
     {
         //printf("0%3o\t%s/\n", status->st_mode&0777, name);
     }
     return 0;
 }

 int main(int argc, char *argv[])
 {
     if(argc == 1)
     {
         ftw(".", list, 1);
     }
     else
     {
         ftw(argv[1], list, 1);
     }

     return 0;
 }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.