Làm cách nào tôi có thể nhận được danh sách các tệp trong một thư mục bằng C hoặc C ++?


593

Làm cách nào tôi có thể xác định danh sách các tệp trong một thư mục từ bên trong mã C hoặc C ++ của mình?

Tôi không được phép thực thi lslệnh và phân tích kết quả từ trong chương trình của mình.


7
Đây là bản sao của 609236
chrish


1
@chrish - Yea nhưng cái này có câu kinh điển "Tôi không được phép thực thi 'ls'"! Đó chính xác là cách tôi cảm nhận năm thứ nhất của Khoa học máy tính. ; D <3 x
James Bedford

1
C và C ++ không cùng ngôn ngữ. Do đó, thủ tục để hoàn thành nhiệm vụ này sẽ khác nhau ở cả hai ngôn ngữ. Vui lòng chọn một và gắn thẻ lại cho phù hợp.
MD XF

2
Và cả hai ngôn ngữ đó (trừ C ++ kể từ C ++ 17) thậm chí không có khái niệm về một thư mục - vì vậy mọi câu trả lời đều có thể phụ thuộc vào HĐH của bạn hoặc vào bất kỳ thư viện trừu tượng nào bạn có thể đang sử dụng.
Toby Speight

Câu trả lời:


809

Trong các tác vụ nhỏ và đơn giản tôi không sử dụng boost, tôi sử dụng dirent.h cũng có sẵn cho 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;
}

Nó chỉ là một tệp tiêu đề nhỏ và thực hiện hầu hết những thứ đơn giản mà bạn cần mà không cần sử dụng một cách tiếp cận dựa trên mẫu lớn như boost (không vi phạm, tôi thích boost!).

Tác giả của lớp tương thích windows là Toni Ronkko. Trong Unix, nó là một tiêu đề tiêu chuẩn.

CẬP NHẬT 2017 :

Trong C ++ 17 hiện có một cách chính thức để liệt kê các tệp của hệ thống tệp của bạn : std::filesystem. Có một câu trả lời tuyệt vời từ Shreevardhan dưới đây với mã nguồn này:

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

5
@ArtOfWarfare: tinydir thậm chí không được tạo khi câu hỏi này được trả lời. Ngoài ra, nó là một trình bao bọc xung quanh dirent (POSIX) và FindFirstFile (Windows), trong khi dirent.h chỉ bao bọc dirent cho các cửa sổ. Tôi nghĩ đó là một sở thích cá nhân, nhưng dirent.h cảm thấy như là một tiêu chuẩn
Peter Parker

7
@JoshC: bởi vì * ent chỉ là một con trỏ trả về của biểu diễn bên trong. bằng cách đóng thư mục, bạn cũng sẽ loại bỏ * ent. Vì * ent chỉ để đọc, đây là một thiết kế lành mạnh, tôi nghĩ vậy.
Peter Parker

42
mọi người nhận được thật !! đây là một câu hỏi từ năm 2009 và nó thậm chí không đề cập đến VS. Vì vậy, đừng chỉ trích rằng IDE hoàn toàn độc quyền (mặc dù khá đẹp) của bạn không hỗ trợ các tiêu chuẩn hệ điều hành cũ hàng thế kỷ. Ngoài ra câu trả lời của tôi cho biết nó "có sẵn" cho windows, không "bao gồm" trong bất kỳ IDE nào từ bây giờ và cho mọi thời điểm ... Tôi khá chắc chắn rằng bạn có thể tải xuống dirent và đưa nó vào một số bao gồm dir và voila ở đó.
Peter Parker

6
Câu trả lời là sai lệch. Nó nên bắt đầu bằng: " ... Tôi sử dụng dirent.h , trong đó lớp tương thích nguồn mở Windows cũng tồn tại ".
rustyx

10
Với C ++ 14 thì có std::experimental::filesystem, với C ++ 17 thì có std::filesystem. Xem câu trả lời của Shreevardhan dưới đây. Vì vậy, không cần thư viện bên thứ 3.
Roi Danton

347

C ++ 17 hiện có một std::filesystem::directory_iterator, có thể được sử dụng như

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

Ngoài ra, std::filesystem::recursive_directory_iteratorcó thể lặp lại các thư mục con là tốt.


28
AFAIK cũng có thể được sử dụng trong C ++ 14, nhưng vẫn còn thử nghiệm : namespace fs = std::experimental::filesystem;. Có vẻ như làm việc ok mặc dù.
PeterK

7
Đây phải là câu trả lời ưa thích cho việc sử dụng hiện tại (bắt đầu với C ++ 17)
diod xanh

4
Chú ý khi chuyển std::filesystem::pathđến std::cout, các dấu ngoặc kép được bao gồm trong đầu ra. Để tránh điều đó, hãy thêm .string()vào đường dẫn để thực hiện một thay đổi rõ ràng thay vì chuyển đổi ngầm định (ở đây std::cout << p.string() << std::endl;). Ví dụ: coliru.stacked-crooking.com/view?id=a55ea60bbd36a8a3
Roi Danton

2
Còn các ký tự NON-ASCII trong tên tệp thì sao? Không nên std::wstringđược sử dụng hoặc loại từ iterator?
anddero

2
Tôi không chắc chắn nếu tôi một mình trong việc này, nhưng không liên kết đến -lstdc++fs, tôi sẽ nhận được một SIGSEGV (Address boundary error). Tôi không thể tìm thấy bất cứ nơi nào trong tài liệu rằng điều này là bắt buộc và trình liên kết cũng không đưa ra bất kỳ manh mối nào. Điều này làm việc cho cả hai g++ 8.3.0clang 8.0.0-3. Có ai có cái nhìn sâu sắc là nơi những thứ như thế này được chỉ định trong tài liệu / thông số kỹ thuật không?
swalog

229

Thật không may, tiêu chuẩn C ++ không định nghĩa một cách làm việc tiêu chuẩn với các tệp và thư mục theo cách này.

Vì không có cách đa nền tảng, nên cách đa nền tảng tốt nhất là sử dụng một thư viện như mô-đun hệ thống tập tin boost .

Phương pháp tăng cường đa nền tảng:

Hàm sau, được cung cấp một đường dẫn thư mục và tên tệp, tìm kiếm đệ quy thư mục và thư mục con của nó cho tên tệp, trả về một bool và nếu thành công, đường dẫn đến tệp được tìm thấy.

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

Nguồn từ trang boost được đề cập ở trên.

Đối với các hệ thống dựa trên Unix / Linux:

Bạn có thể sử dụng opendir / readdir / closir .

Mã mẫu tìm kiếm một thư mục để nhập `` name '' là:

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;

Mã nguồn từ các trang người đàn ông ở trên.

Đối với một hệ thống dựa trên windows:

Bạn có thể sử dụng các hàm Win32 API FindFirstFile / FindNextFile / FindClose .

Ví dụ C ++ sau đây cho bạn thấy việc sử dụng tối thiểu 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);
   }
}

Mã nguồn từ các trang msDN ở trên.


Cách sử dụng:FindFirstFile(TEXT("D:\\IMAGE\\MYDIRECTORY\\*"), &findFileData);
Toàn cảnh

7
Với C ++ 14 thì có std::experimental::filesystem, với C ++ 17 std::filesystem, có chức năng tương tự như boost (các lib được lấy từ boost). Xem câu trả lời của Shreevardhan dưới đây.
Roi Danton

Đối với các cửa sổ, hãy tham khảo docs.microsoft.com/en-us/windows/desktop/FileIO/iêu để biết chi tiết
FindOutIslamNow

90

Một chức năng là đủ, bạn không cần sử dụng bất kỳ thư viện của bên thứ 3 nào (đối với 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;
}

PS: như được đề cập bởi @Sebastian, bạn có thể thay đổi *.*để *.extchỉ nhận các tệp EXT (tức là một loại cụ thể) trong thư mục đó.


20
Giải pháp này nếu nền tảng cụ thể. Đó là lý do bạn cần thư viện của bên thứ 3.
kraxor

9
@kraxor Có, nó chỉ hoạt động trong Windows, nhưng OP không bao giờ yêu cầu có giải pháp đa nền tảng. BTW, tôi luôn thích chọn thứ gì đó mà không sử dụng thư viện thứ 3 (nếu có thể).
herohuyongtao

7
@herohuyongtao OP không bao giờ chỉ định một nền tảng và việc đưa ra một giải pháp phụ thuộc nhiều vào nền tảng cho một câu hỏi chung có thể gây hiểu nhầm. .
kraxor

1
@herohuyongtao OP đã đề cập anh ấy không thể phân tích ls, có nghĩa là anh ấy có lẽ đang ở trên unix .. dù sao, câu trả lời tốt cho Windows.
Thomas

2
Tôi đã kết thúc bằng cách sử dụng một std::vector<std::wstring>và sau đó fileName.c_str()thay vì một chuỗi các chuỗi, sẽ không biên dịch.
PerryC

51

Đối với một giải pháp chỉ C, xin vui lòng kiểm tra này. Nó chỉ yêu cầu một tiêu đề phụ:

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

Một số lợi thế so với các tùy chọn khác:

  • Nó có thể mang theo - kết thúc tốt đẹp POSIX dirent và Windows FindFirstFile
  • Nó sử dụng readdir_rkhi có sẵn, có nghĩa là nó (thường)
  • Hỗ trợ Windows UTF-16 thông qua các UNICODEmacro tương tự
  • Nó là C90 nên ngay cả các trình biên dịch rất cổ xưa cũng có thể sử dụng nó

2
Đề nghị rất tốt đẹp. Tôi chưa thử nghiệm nó trên máy tính windows nhưng nó hoạt động rất tốt trên OS X.
ArtOfWarfare

Thư viện không hỗ trợ std :: string, vì vậy bạn không thể truyền file.c_str () cho tinydir_open. Nó đưa ra lỗi C2664 trong quá trình biên dịch trên msvc 2015 trong trường hợp này.
Stepan Yakovenko

33

Tôi khuyên bạn nên sử dụng globvới trình bao bọc có thể tái sử dụng này. Nó tạo ra một vector<string>đường dẫn tệp tương ứng với mẫu hình cầu:

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

Mà sau đó có thể được gọi với một mẫu ký tự đại diện hệ thống bình thường, chẳng hạn như:

vector<string> files = globVector("./*");

2
Kiểm tra rằng global () trả về 0.
Camille Goudeseune

Tôi muốn sử dụng global.h như bạn đề nghị. Tuy nhiên, tôi không thể bao gồm tệp .h: Nó nói No such file or directory. Bạn có thể cho tôi biết làm thế nào để giải quyết vấn đề này xin vui lòng?
Tofuw 23/2/2016

Lưu ý rằng thói quen này chỉ đi sâu một cấp (không có đệ quy). Nó cũng không làm một kiểm tra nhanh để xác định xem đó là một tập tin hoặc thư mục mà bạn có thể làm một cách dễ dàng bằng cách chuyển GLOB_TILDEvới GLOB_TILDE | GLOB_MARKvà sau đó kiểm tra các đường dẫn kết thúc bằng một dấu gạch chéo. Bạn sẽ phải sửa đổi nó nếu bạn cần.
Volomike

Là đa nền tảng này tương thích?
Nikhil Augustine

Thật không may, bạn không thể tìm thấy các tập tin ẩn thống nhất thông qua glob.
LmTinyToon

23

Đây là một mã rất đơn giản trong C++11việc sử dụng boost::filesystemthư viện để lấy tên tệp trong một thư mục (không bao gồm tên thư mục):

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

Đầu ra giống như:

file1.txt
file2.dat

Xin chào, và tôi có thể lấy thư viện này ở đâu?
Alexander Leon VI

2
@Alexander De Leon: Bạn có thể lấy thư viện này tại trang web của họ boost.org , đọc hướng dẫn bắt đầu trước, sau đó sử dụng boost::filesystemthư viện boost.org/doc/libs/1_58_0/libs/filesystem/doc/index.htm
Bad

23

Tại sao không sử dụng 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;
}

Đây có thể là một câu trả lời tốt hơn nếu bạn giải thích các yêu cầu bao gồm.
Volomike

2
Kiểm tra rằng global () trả về 0!
orbitcowboy

Điều này thật tốt khi bạn biết tệp bạn đang tìm kiếm, chẳng hạn như * .txt
Kemin Zhou

20

Tôi nghĩ rằng, đoạn dưới đây có thể được sử dụng để liệt kê tất cả các tệp.

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

Sau đây là cấu trúc của dirent struct

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

Tôi thích cái này
selfboot

10

Hãy thử tăng cường cho phương pháp nền tảng x

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

hoặc chỉ sử dụng công cụ tập tin cụ thể hệ điều hành của bạn.


Mặc dù liên kết này có thể trả lời câu hỏi, tốt hơn là bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi. - Từ đánh giá
ice1000

@ ice1000 Nghiêm túc? Câu hỏi và trả lời này là từ năm 2009
Tim

8

Kiểm tra lớp này sử dụng api win32. Chỉ cần xây dựng một thể hiện bằng cách cung cấp foldernametừ đó bạn muốn liệt kê sau đó gọi getNextFilephương thức để lấy cái tiếp theo filenametừ thư mục. Tôi nghĩ rằng nó cần windows.hstdio.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;
    }

};

6

Hướng dẫn sử dụng GNU FTW

http://www.gnu.org/software/libc/manual/html_node/Simple-Directory-Lister.html#Simple-Directory-Lister

Ngoài ra, đôi khi thật tốt khi đi thẳng vào nguồn (ý định chơi chữ). Bạn có thể học được rất nhiều bằng cách xem xét các phần bên trong của một số lệnh phổ biến nhất trong Linux. Tôi đã thiết lập một bản sao đơn giản của coreutils GNU trên github (để đọc).

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

Có thể điều này không giải quyết được Windows, nhưng một số trường hợp sử dụng các biến thể Unix có thể có bằng các phương pháp này.

Mong rằng sẽ giúp ...


5

Shreevardhan trả lời hoạt động tuyệt vời. Nhưng nếu bạn muốn sử dụng nó trong c ++ 14, hãy thay đổinamespace fs = experimental::filesystem;

I E,

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

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

3

Tôi hy vọng mã này giúp bạn.

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

4
-1. string2wchar_ttrả về địa chỉ của một biến cục bộ Ngoài ra, có lẽ bạn nên sử dụng các phương thức chuyển đổi có sẵn trong WinAPI thay vì viết các phương thức của riêng bạn.
Daniel Kamil Kozar

3

Việc thực hiện này thực hiện mục đích của bạn, tự động điền vào một chuỗi các chuỗi với nội dung của thư mục được chỉ định.

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

Làm thế nào tôi sẽ gọi đây? Tôi nhận được segfaults khi tôi cố chạy chức năng này trên ifkhối đầu tiên . Tôi đang gọi nó vớichar **list; int numItems; exploreDirectory("/folder",list, numItems);
Hal T

2

Điều này làm việc cho tôi. Tôi xin lỗi nếu tôi không thể nhớ nguồn. Nó có lẽ là từ một trang người đàn ông.

#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/");
}

2

bạn có thể nhận được tất cả các tệp trực tiếp trong thư mục gốc của mình bằng cách sử dụng std :: thử nghiệm :: filesystem :: library_iterator (). Sau đó, đọc tên của các pathfiles.

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

2

Câu trả lời này sẽ phù hợp với người dùng Windows gặp khó khăn khi làm việc với Visual Studio với bất kỳ câu trả lời nào khác.

  1. Tải xuống tệp dirent.h từ trang github. Nhưng tốt hơn là chỉ sử dụng tệp Raw dirent.h và làm theo các bước của tôi dưới đây (đó là cách tôi làm cho nó hoạt động).

    Trang Github cho dirent.h cho Windows: Trang Github cho dirent.h

    Raw Dirent File: Raw dirent.h File

  2. Chuyển đến dự án của bạn và Thêm một mục mới ( Ctrl+ Shift+ A). Thêm một tệp tiêu đề (.h) và đặt tên là dirent.h.

  3. Dán mã tệp dirent.h thô vào tiêu đề của bạn.

  4. Bao gồm "dirent.h" trong mã của bạn.

  5. Đặt void filefinder()phương thức dưới đây vào mã của bạn và gọi nó từ mainhàm của bạn hoặc chỉnh sửa hàm theo cách bạn muốn sử dụng.

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

1

Hệ thống gọi nó!

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

Sau đó chỉ cần đọc các tập tin.

EDIT: Câu trả lời này nên được coi là một hack, nhưng nó thực sự hoạt động (mặc dù theo cách cụ thể của nền tảng) nếu bạn không có quyền truy cập vào các giải pháp thanh lịch hơn.


7
Tôi không được phép thực hiện lệnh 'ls' và phân tích kết quả từ trong chương trình của mình. Tôi biết sẽ có ai đó sẽ gửi một cái gì đó như thế này ...
yyny

1

Do các tệp và thư mục con của một thư mục thường được lưu trữ trong cấu trúc cây, nên một cách trực quan là sử dụng thuật toán DFS để duyệt qua từng đệ quy. Dưới đây là một ví dụ trong hệ điều hành windows bằng cách sử dụng các hàm tệp cơ bản trong io.h. Bạn có thể thay thế các chức năng này trong nền tảng khác. Điều tôi muốn bày tỏ là ý tưởng cơ bản về DFS đáp ứng hoàn hảo vấn đề này.

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

1

Tôi đã cố gắng làm theo ví dụ được đưa ra trong cả hai câu trả lời và có thể đáng lưu ý rằng nó xuất hiện như thể std::filesystem::directory_entryđã được thay đổi để không có sự quá tải của <<toán tử. Thay vì std::cout << p << std::endl;tôi phải sử dụng những điều sau đây để có thể biên dịch và làm cho nó hoạt động:

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

cố gắng tự mình vượt qua pđể std::cout <<dẫn đến một lỗi quá tải bị thiếu.


1

Dựa trên những gì herohuyongtao đã đăng và một vài bài viết khác:

http://www.cplusplus.com/forum/general/39766/

Loại đầu vào dự kiến ​​của FindFirstFile là gì?

Làm thế nào để chuyển đổi chuỗi thành chuỗi?

Đây là một giải pháp Windows.

Vì tôi muốn truyền vào chuỗi std :: và trả về một vectơ chuỗi, tôi phải thực hiện một vài chuyển đổi.

#include <string>
#include <Windows.h>
#include <vector>
#include <locale>
#include <codecvt>

std::vector<std::string> listFilesInDir(std::string path)
{
    std::vector<std::string> names;
    //Convert string to wstring
    std::wstring search_path = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(path);
    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)) 
            {
                //convert from wide char to narrow char array
                char ch[260];
                char DefChar = ' ';
                WideCharToMultiByte(CP_ACP, 0, fd.cFileName, -1, ch, 260, &DefChar, NULL);
                names.push_back(ch);
            }
        } 
        while (::FindNextFile(hFind, &fd));
        ::FindClose(hFind);
    }
    return names;
}

Nếu bạn biết rằng bạn sẽ chỉ sử dụng multibyte bạn có thể sử dụng WIN32_FIND_DATAA, FindFirstFileAFindNextFileA. Sau đó, sẽ không cần phải chuyển đổi kết quả thành multibyte hoặc Input thành unicode.
Kiran Thilak

0

Chỉ cần một cái gì đó mà tôi muốn chia sẻ và cảm ơn bạn đã đọc tài liệu. Chơi xung quanh với chức năng một chút để hiểu nó. Bạn có thể thích nó. e là viết tắt của phần mở rộng, p là cho đường dẫn và s là cho dấu phân cách đường dẫn.

Nếu đường dẫn được truyền mà không có dấu phân cách kết thúc, dấu phân cách sẽ được thêm vào đường dẫn. Đối với phần mở rộng, nếu một chuỗi rỗng được nhập vào thì hàm sẽ trả về bất kỳ tệp nào không có phần mở rộng trong tên của nó. Nếu một ngôi sao duy nhất được nhập vào, tất cả các tệp trong thư mục sẽ được trả về. Nếu độ dài e lớn hơn 0 nhưng không phải là một dấu * thì dấu chấm sẽ được thêm vào e nếu e không chứa dấu chấm ở vị trí 0.

Đối với một giá trị trả lại. Nếu một bản đồ có độ dài bằng không được trả về thì không tìm thấy gì nhưng thư mục đã mở. Nếu chỉ mục 999 có sẵn từ giá trị trả về nhưng kích thước bản đồ chỉ là 1 thì điều đó có nghĩa là có vấn đề với việc mở đường dẫn thư mục.

Lưu ý rằng để hiệu quả, chức năng này có thể được chia thành 3 chức năng nhỏ hơn. Trên hết, bạn có thể tạo một chức năng người gọi sẽ phát hiện chức năng mà nó sẽ gọi dựa trên đầu vào. Tại sao điều đó hiệu quả hơn? Cho biết nếu bạn định lấy tất cả mọi thứ là một tệp, thực hiện phương thức đó, chức năng con được xây dựng để lấy tất cả các tệp sẽ chỉ lấy tất cả các tệp đó và không cần phải đánh giá bất kỳ điều kiện không cần thiết nào khác mỗi khi tìm thấy tệp.

Điều đó cũng sẽ áp dụng cho khi bạn lấy các tệp không có tiện ích mở rộng. Một hàm được xây dựng cụ thể cho mục đích đó sẽ chỉ đánh giá thời tiết nếu đối tượng được tìm thấy là một tệp và sau đó có hay không nếu tên của tệp có dấu chấm trong đó.

Việc tiết kiệm có thể không nhiều nếu bạn chỉ đọc các thư mục không có quá nhiều tệp. Nhưng nếu bạn đang đọc một lượng lớn thư mục hoặc nếu thư mục có vài trăm nghìn tệp, thì đó có thể là một khoản tiết kiệm rất lớn.

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

0
#include<iostream>
#include <dirent.h>
using namespace std;
char ROOT[]={'.'};

void listfiles(char* path){
    DIR * dirp = opendir(path);
    dirent * dp;
    while ( (dp = readdir(dirp)) !=NULL ) {
         cout << dp->d_name << " size " << dp->d_reclen<<std::endl;
    }
    (void)closedir(dirp);
}

int main(int argc, char **argv)
{
    char* path;
    if (argc>1) path=argv[1]; else path=ROOT;

    cout<<"list files in ["<<path<<"]"<<std::endl;
    listfiles(path);

    return 0;
}

-1

Điều này làm việc cho tôi. Nó viết một tệp chỉ có tên (không có đường dẫn) của tất cả các tệp. Sau đó, nó đọc tệp txt đó và in nó cho bạn.

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

    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.