C 또는 C ++를 사용하여 디렉토리에서 파일 목록을 어떻게 얻을 수 있습니까?

StackOverflow https://stackoverflow.com/questions/612097

  •  03-07-2019
  •  | 
  •  

문제

내 C 또는 C ++ 코드 내부에서 디렉토리의 파일 목록을 어떻게 결정할 수 있습니까?

나는 실행할 수 없다 ls 내 프로그램 내에서 결과를 명령하고 구문 분석하십시오.

도움이 되었습니까?

해결책

작고 간단한 작업에서는 부스트를 사용하지 않습니다. 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;
}

그것은 단지 작은 헤더 파일 일 뿐이며 부스트와 같은 큰 템플릿 기반 접근법을 사용하지 않고 필요한 간단한 것들 대부분을 수행합니다 (공격 없음, 부스트를 좋아합니다!).

Windows 호환성 레이어의 저자는 Toni Ronkko입니다. 유닉스에서는 표준 헤더입니다.

업데이트 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;
}

다른 팁

C ++ 17은 이제 a 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 하위 디렉터도 반복 할 수 있습니다.

불행히도 C ++ 표준은 이러한 방식으로 파일과 폴더로 작업하는 표준 방식을 정의하지 않습니다.

크로스 플랫폼 방식이 없기 때문에 가장 좋은 크로스 플랫폼 방식은 파일 시스템 모듈을 향상시킵니다.

크로스 플랫폼 부스트 방법 :

디렉토리 경로와 파일 이름이 주어지면 다음 기능은 파일 이름에 대한 디렉토리와 하위 디렉토리를 재귀 적으로 검색하고 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 / 폐쇄.

``이름 ''항목 디렉토리를 검색하는 샘플 코드는 다음과 같습니다.

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 페이지의 소스 코드.

하나의 기능으로는 충분하므로 3 자 라이브러리 (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;
}

추신 : @sebastian이 언급했듯이 *.* 에게 *.ext 해당 디렉토리에서 ext-files (예 : 특정 유형의) 만 얻기 위해.

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

다른 옵션에 비해 몇 가지 장점 :

  • 휴대용 - 포스 포스 디 렌트 및 Windows FindFirstFile.
  • 사용합니다 readdir_r 사용 가능한 경우, 이는 (보통) ThreadSafe입니다.
  • 동일한 것을 통해 Windows UTF-16을 지원합니다 UNICODE 매크로
  • C90이므로 매우 고대 컴파일러조차도 사용할 수 있습니다.

사용하는 것이 좋습니다 glob 이 재사용 가능한 래퍼와 함께. a vector<string> 글로벌 패턴에 맞는 파일 경로에 해당합니다.

#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("./*");

다음은 매우 간단한 코드입니다 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

사용하지 않는 이유 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;
}

아래 스 니펫을 사용하여 모든 파일을 나열 할 수 있다고 생각합니다.

#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 {
    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 */
};

X- 플랫폼 방법에 대한 부스트를 사용해보십시오

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

또는 OS 특정 파일을 사용하십시오.

Win32 API를 사용하는이 클래스를 확인하십시오. 제공하여 인스턴스를 구성하십시오 foldername 목록을 원하는 다음 전화하십시오 getNextFile 다음을 얻는 방법 filename 디렉토리에서. 나는 그것이 필요하다고 생각합니다 windows.h 그리고 stdio.h.

class FileGetter{
    WIN32_FIND_DATAA found; 
    HANDLE hfind;
    char folderstar[255];       
    int chk;

public:
    FileGetter(char* folder){       
        sprintf(folderstar,"%s\\*.*",folder);
        hfind = FindFirstFileA(folderstar,&found);
        //skip .
        FindNextFileA(hfind,&found);        
    }

    int getNextFile(char* fname){
        //skips .. when called for the first time
        chk=FindNextFileA(hfind,&found);
        if (chk)
            strcpy(fname, found.cFileName);     
        return chk;
    }

};

GNU 매뉴얼 FTW

http://www.gnu.org/software/libc/manual/html_node/simple-directory-lister.html#simple-directory-lister

또한 때로는 출처로 바로 가기가 좋습니다 (말장난 의도). Linux에서 가장 일반적인 명령 중 일부를 보면서 많은 것을 배울 수 있습니다. 나는 Github에 GNU의 Coreutils의 간단한 거울을 설정했습니다 (독서 용).

https://github.com/homer6/gnu_coreutils/blob/master/src/ls.c

어쩌면 이것은 Windows를 다루지 않지만 이러한 방법을 사용하여 UNIX 변형을 사용하는 여러 가지 사례를 사용할 수 있습니다.

도움이되기를 바랍니다 ...

char **getKeys(char *data_dir, char* tablename, int *num_keys)
{
    char** arr = malloc(MAX_RECORDS_PER_TABLE*sizeof(char*));
int i = 0;
for (;i < MAX_RECORDS_PER_TABLE; i++)
    arr[i] = malloc( (MAX_KEY_LEN+1) * sizeof(char) );  


char *buf = (char *)malloc( (MAX_KEY_LEN+1)*sizeof(char) );
snprintf(buf, MAX_KEY_LEN+1, "%s/%s", data_dir, tablename);

DIR* tableDir = opendir(buf);
struct dirent* getInfo;

readdir(tableDir); // ignore '.'
readdir(tableDir); // ignore '..'

i = 0;
while(1)
{


    getInfo = readdir(tableDir);
    if (getInfo == 0)
        break;
    strcpy(arr[i++], getInfo->d_name);
}
*(num_keys) = i;
return arr;
}

이 코드가 도움이되기를 바랍니다.

#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

string wchar_t2string(const wchar_t *wchar)
{
    string str = "";
    int index = 0;
    while(wchar[index] != 0)
    {
        str += (char)wchar[index];
        ++index;
    }
    return str;
}

wchar_t *string2wchar_t(const string &str)
{
    wchar_t wchar[260];
    int index = 0;
    while(index < str.size())
    {
        wchar[index] = (wchar_t)str[index];
        ++index;
    }
    wchar[index] = 0;
    return wchar;
}

vector<string> listFilesInDirectory(string directoryName)
{
    WIN32_FIND_DATA FindFileData;
    wchar_t * FileName = string2wchar_t(directoryName);
    HANDLE hFind = FindFirstFile(FileName, &FindFileData);

    vector<string> listFileNames;
    listFileNames.push_back(wchar_t2string(FindFileData.cFileName));

    while (FindNextFile(hFind, &FindFileData))
        listFileNames.push_back(wchar_t2string(FindFileData.cFileName));

    return listFileNames;
}

void main()
{
    vector<string> listFiles;
    listFiles = listFilesInDirectory("C:\\*.txt");
    for each (string str in listFiles)
        cout << str << endl;
}

Shreevardhan 답변은 훌륭하게 작동합니다. 하지만 C ++ 14에서 사용하려면 변경하십시오. namespace fs = experimental::filesystem;

즉,

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

using namespace std;
namespace fs = experimental::filesystem;

int main()
{
    string path = "C:\\splits\\";
    for (auto & p : fs::directory_iterator(path))
        cout << p << endl;
    int n;
    cin >> n;
}

이 구현은 지정된 디렉토리의 내용으로 문자열 배열을 동적으로 채우는 목적을 실현합니다.

int exploreDirectory(const char *dirpath, char ***list, int *numItems) {
    struct dirent **direntList;
    int i;
    errno = 0;

    if ((*numItems = scandir(dirpath, &direntList, NULL, alphasort)) == -1)
        return errno;

    if (!((*list) = malloc(sizeof(char *) * (*numItems)))) {
        fprintf(stderr, "Error in list allocation for file list: dirpath=%s.\n", dirpath);
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < *numItems; i++) {
        (*list)[i] = stringDuplication(direntList[i]->d_name);
    }

    for (i = 0; i < *numItems; i++) {
        free(direntList[i]);
    }

    free(direntList);

    return 0;
}

이것은 나를 위해 작동합니다. 출처를 기억할 수 없다면 죄송합니다. 아마도 남자 페이지에서 온 것일 것입니다.

#include <ftw.h>

int AnalizeDirectoryElement (const char *fpath, 
                            const struct stat *sb,
                            int tflag, 
                            struct FTW *ftwbuf) {

  if (tflag == FTW_F) {
    std::string strFileName(fpath);

    DoSomethingWith(strFileName);
  }
  return 0; 
}

void WalkDirectoryTree (const char * pchFileName) {

  int nFlags = 0;

  if (nftw(pchFileName, AnalizeDirectoryElement, 20, nFlags) == -1) {
    perror("nftw");
  }
}

int main() {
  WalkDirectoryTree("some_dir/");
}

std :: 실험 :: fileSystem :: directory_iterator ()를 사용하여 루트 디렉토리에 모든 파일을 모두 가져올 수 있습니다. 그런 다음이 패스 파일의 이름을 읽으십시오.

#include <iostream>
#include <filesystem>
#include <string>
#include <direct.h>
using namespace std;
namespace fs = std::experimental::filesystem;
void ShowListFile(string path)
{
for(auto &p: fs::directory_iterator(path))  /*get directory */
     cout<<p.path().filename()<<endl;   // get file name
}

int main() {

ShowListFile("C:/Users/dell/Pictures/Camera Roll/");
getchar();
return 0;
}

시스템 호출!

system( "dir /b /s /a-d * > file_names.txt" );

그런 다음 파일을 읽으십시오.

편집 :이 답변은 해킹으로 간주되어야하지만, 더 우아한 솔루션에 액세스 할 수 없다면 실제로 작동합니다 (플랫폼 특정 방식으로).

디렉토리의 파일 및 서브 디렉토리는 일반적으로 트리 구조에 저장되므로 직관적 인 방법은 DFS 알고리즘을 사용하여 각각을 재귀 적으로 통과하는 것입니다. 다음은 io.h에서 기본 파일 함수를 사용하여 Windows 운영 체제의 예입니다. 다른 플랫폼에서 이러한 기능을 교체 할 수 있습니다. 내가 표현하고 싶은 것은 DFS의 기본 아이디어 가이 문제를 완벽하게 충족한다는 것입니다.

#include<io.h>
#include<iostream.h>
#include<string>
using namespace std;

void TraverseFilesUsingDFS(const string& folder_path){
   _finddata_t file_info;
   string any_file_pattern = folder_path + "\\*";
   intptr_t handle = _findfirst(any_file_pattern.c_str(),&file_info);
   //If folder_path exsist, using any_file_pattern will find at least two files "." and "..", 
   //of which "." means current dir and ".." means parent dir
   if (handle == -1){
       cerr << "folder path not exist: " << folder_path << endl;
       exit(-1);
   }
   //iteratively check each file or sub_directory in current folder
   do{
       string file_name=file_info.name; //from char array to string
       //check whtether it is a sub direcotry or a file
       if (file_info.attrib & _A_SUBDIR){
            if (file_name != "." && file_name != ".."){
               string sub_folder_path = folder_path + "\\" + file_name;                
               TraverseFilesUsingDFS(sub_folder_path);
               cout << "a sub_folder path: " << sub_folder_path << endl;
            }
       }
       else
            cout << "file name: " << file_name << endl;
    } while (_findnext(handle, &file_info) == 0);
    //
    _findclose(handle);
}

이 답변은 다른 답변과 함께 Visual Studio를 사용하는 데 어려움이있는 Windows 사용자에게는 효과가 있습니다.

  1. Github 페이지에서 Dirent.h 파일을 다운로드하십시오. 그러나 Raw Dirent.h 파일을 사용하고 아래의 단계를 따르는 것이 좋습니다 (제가 작동하는 방법입니다).

    Windows 용 Github 페이지 : Windows 용 Github 페이지 : Dirent.h의 Github 페이지

    RAW DIRENT 파일 : RAW DIRENT.H 파일

  2. 프로젝트로 가서 새 항목을 추가하십시오 (Ctrl 키+옮기다+). 헤더 파일 (.H)을 추가하고 이름을 지정하십시오.

  3. 붙여 넣기 RAW DIRENT.H 파일 헤더에 코드.

  4. 코드에 "Dirent.h"를 포함하십시오.

  5. 아래를 넣으십시오 void filefinder() 코드에서 메소드를 호출하십시오 main 기능을 사용하는 방식을 기능하거나 편집하십시오.

    #include <stdio.h>
    #include <string.h>
    #include "dirent.h"
    
    string path = "C:/folder"; //Put a valid path here for folder
    
    void filefinder()
    {
        DIR *directory = opendir(path.c_str());
        struct dirent *direntStruct;
    
        if (directory != NULL) {
            while (direntStruct = readdir(directory)) {
                printf("File Name: %s\n", direntStruct->d_name); //If you are using <stdio.h>
                //std::cout << direntStruct->d_name << std::endl; //If you are using <iostream>
            }
        }
        closedir(directory);
    }
    

나는 주어진 예를 따르려고 노력했다 둘 다 답변 그리고 마치 마치 마치 마치 보인다는 점에 주목할 가치가 있습니다 std::filesystem::directory_entry 과부하가없는 것으로 변경되었습니다. << 운영자. 대신에 std::cout << p << std::endl; 컴파일하고 작동하도록하기 위해 다음을 사용해야했습니다.

#include <iostream>
#include <filesystem>
#include <string>
namespace fs = std::filesystem;

int main() {
    std::string path = "/path/to/directory";
    for(const auto& p : fs::directory_iterator(path))
        std::cout << p.path() << std::endl;
}

통과하려고합니다 p 그 자체로 std::cout << 오버로드 오류가 누락되었습니다.

내가 공유하고 싶은 것이며 독서 자료에 감사드립니다. 그것을 이해하기 위해 약간의 기능을 가지고 놀아요. 당신은 그것을 좋아할 수도 있습니다. e는 확장을 위해 서 있었고, p는 경로, s는 경로 분리기를위한 것입니다.

분리기를 끝내지 않고 경로가 통과되면 분리기가 경로에 추가됩니다. 확장자의 경우 빈 문자열이 입력되면 함수는 이름에 확장자가없는 파일을 반환합니다. 디렉토리의 모든 파일보다 단일 스타가 입력 된 경우 디렉토리가 반환됩니다. e 길이가 0보다 크지 만 단일 *이 아닌 경우 e가 0 위치에 점이 포함되어 있지 않은 경우 점이 e로 선정됩니다.

반환 가치를 위해. 제로 길이의 맵이 반환되면 아무것도 발견되지 않았지만 디렉토리는 괜찮습니다. 인덱스 999를 반환 값에서 사용할 수 있지만 맵 크기가 1이면 디렉토리 경로를 열는 데 문제가 있음을 의미합니다.

효율성을 위해이 기능은 3 개의 작은 함수로 나눌 수 있습니다. 또한 입력에 따라 호출 할 기능을 감지하는 발신자 함수를 만들 수 있습니다. 왜 더 효율적인가? 파일 인 모든 것을 가져 오려면 해당 메소드를 수행하면 모든 파일을 잡기 위해 구축 된 하위 기능을 수행하면 파일이 모두 잡히고 파일을 찾을 때마다 다른 불필요한 조건을 평가할 필요가 없습니다.

확장이없는 파일을 잡을 때도 적용됩니다. 해당 목적에 대한 특정 구축 기능은 발견 된 개체가 파일 인 경우 날씨에 대해서만 평가하고 파일 이름에 점이 있는지 여부가 있는지 여부입니다.

파일이 많지 않은 디렉토리 만 읽는 경우 저장은 그리 많지 않을 수 있습니다. 그러나 대량의 디렉토리를 읽거나 디렉토리에 수십만 개의 파일이있는 경우 큰 절약이 될 수 있습니다.

#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <dirent.h>
#include <map>

std::map<int, std::string> getFile(std::string p, std::string e = "", unsigned char s = '/'){
    if ( p.size() > 0 ){
        if (p.back() != s) p += s;
    }
    if ( e.size() > 0 ){
        if ( e.at(0) != '.' && !(e.size() == 1 && e.at(0) == '*') ) e = "." + e;
    }

    DIR *dir;
    struct dirent *ent;
    struct stat sb;
    std::map<int, std::string> r = {{999, "FAILED"}};
    std::string temp;
    int f = 0;
    bool fd;

    if ( (dir = opendir(p.c_str())) != NULL ){
        r.erase (999);
        while ((ent = readdir (dir)) != NULL){
            temp = ent->d_name;
            fd = temp.find(".") != std::string::npos? true : false;
            temp = p + temp;

            if (stat(temp.c_str(), &sb) == 0 && S_ISREG(sb.st_mode)){
                if ( e.size() == 1 && e.at(0) == '*' ){
                    r[f] = temp;
                    f++;
                } else {
                    if (e.size() == 0){
                        if ( fd == false ){
                            r[f] = temp;
                            f++;
                        }
                        continue;
                    }

                    if (e.size() > temp.size()) continue;

                    if ( temp.substr(temp.size() - e.size()) == e ){
                        r[f] = temp;
                        f++;
                    }
                }
            }
        }

        closedir(dir);
        return r;
    } else {
        return r;
    }
}

void printMap(auto &m){
    for (const auto &p : m) {
        std::cout << "m[" << p.first << "] = " << p.second << std::endl;
    }
}

int main(){
    std::map<int, std::string> k = getFile("./", "");
    printMap(k);
    return 0;
}

이것은 나를 위해 효과가있었습니다. 모든 파일의 이름 (경로 없음) 만있는 파일을 씁니다. 그런 다음 TXT 파일을 읽고 인쇄합니다.

void DisplayFolderContent()
    {

        system("dir /n /b * > file_names.txt");
        char ch;
        std::fstream myStream("file_names.txt", std::fstream::in);
        while (myStream.get(ch))
        {
            std::cout << ch;
        }

    }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top