2008-09-15 52 views
87

Làm thế nào để bạn lặp qua tất cả các tệp/thư mục đệ quy trong C++ chuẩn?Làm thế nào để bạn lặp qua tất cả các tệp/thư mục đệ quy trong C++ chuẩn?

+1

Không chuẩn C++: http://pocoproject.org/docs/Poco.DirectoryIterator.html –

+1

này nên sớm nằm trong tiêu chuẩn thông qua [TS hệ thống tập tin] (http://en.cppreference.com/w/cpp/experimental/fs), với [recursive_directory_iterator] (http: //en.cppreference.com/w/cpp/thử nghiệm/fs/recursive_directory_iterator) –

+0

Nếu sử dụng thư viện C chuẩn không nhận được cách gọi một chương trình C++ là 'chuẩn', [nftw()] (https: //linux.die .net/người đàn ông/3/nftw). Đây là một ví dụ thực tế (https://github.com/six-k/dtreetrawl/blob/f7c1d320225ee754b96fef28bb0774a2c34b91b8/dtreetrawl.c#L473) –

Trả lời

91

Trong tiêu chuẩn C++, về mặt kỹ thuật không có cách nào để làm điều này kể từ khi chuẩn C++ không có quan niệm về thư mục. Nếu bạn muốn mở rộng mạng của mình một chút, bạn có thể muốn xem xét sử dụng Boost.FileSystem. Điều này đã được chấp nhận để đưa vào TR2, do đó, điều này mang đến cho bạn cơ hội tốt nhất để giữ cho việc triển khai của bạn càng gần với tiêu chuẩn càng tốt.

Một ví dụ, lấy trực tiếp từ trang web:

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++ không có khái niệm về tệp? Điều gì về std :: fstream? Hoặc fopen? – Kevin

+28

tệp, không phải thư mục –

+22

Cập nhật liên quan đến phiên bản tăng mới nhất: Trong trường hợp bất cứ ai tình cờ gặp câu trả lời này, lần tăng mới nhất bao gồm tăng cường lớp tiện lợi :: recursive_directory_iterator để viết vòng lặp ở trên với cuộc gọi đệ quy rõ ràng không còn cần thiết nữa. Liên kết: http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/reference.html#Class-recursive_directory_iterator – JasDev

2

Bạn cần gọi các chức năng dành riêng cho hệ điều hành để duyệt qua hệ thống tệp, như open()readdir(). Tiêu chuẩn C không chỉ định bất kỳ hàm liên quan đến hệ thống tập tin nào.

+0

Còn C++ thì sao? Có bất kỳ chức năng nào như vậy trong iostream không? –

+2

Chỉ dành cho tệp. Không có bất kỳ loại "hiển thị cho tôi tất cả các tập tin trong một thư mục" chức năng. –

+1

@ 1800: Thư mục là các tệp. –

1

Bạn không có. Standard C++ không hiển thị khái niệm về thư mục. Cụ thể là nó không cung cấp bất kỳ cách nào để liệt kê tất cả các tệp trong một thư mục.

Một cuộc tấn công khủng khiếp sẽ là sử dụng các cuộc gọi system() và phân tích cú pháp kết quả. Giải pháp hợp lý nhất là sử dụng một số loại thư viện đa nền tảng như Qt hoặc thậm chí POSIX.

33

Nếu sử dụng API Win32, bạn có thể sử dụng các chức năng FindFirstFileFindNextFile.

http://msdn.microsoft.com/en-us/library/aa365200(VS.85).aspx

Đối traversal đệ quy các thư mục, bạn phải kiểm tra mỗi WIN32_FIND_DATA.dwFileAttributes để kiểm tra xem các bit FILE_ATTRIBUTE_DIRECTORY được thiết lập. Nếu bit được đặt thì bạn có thể đệ quy gọi hàm bằng thư mục đó. Ngoài ra, bạn có thể sử dụng một ngăn xếp để cung cấp cùng một hiệu ứng của một cuộc gọi đệ quy nhưng tránh tràn ngăn xếp cho cây con đường rất dài.

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

Mất bao lâu để bạn viết nó? Tôi nghĩ rằng nó sẽ mất ít thời gian hơn để dán C++ vào python và làm điều đó trong một dòng. –

+1

Đây là một giải pháp tốt, không đệ quy (đôi khi có ích!). –

+1

Btw, nếu ai đó muốn chỉnh sửa chương trình một chút để chấp nhận tham số dòng lệnh argv [1] cho đường dẫn thay vì đường dẫn cứng ("F: \\ cvsrepos"), chữ ký cho main (int, char) sẽ thay đổi để wmain (int, wchar_t) như thế này: int wmain (int argc, wchar_t * argv []) – JasDev

2

Bạn không. Tiêu chuẩn C++ không có khái niệm về thư mục. Việc triển khai thực hiện để biến một chuỗi thành một xử lý tệp. Nội dung của chuỗi đó và những gì nó ánh xạ tới là hệ điều hành phụ thuộc. Hãy nhớ rằng C++ có thể được sử dụng để viết hệ điều hành đó, vì vậy nó được sử dụng ở mức yêu cầu làm thế nào để lặp qua một thư mục chưa được xác định (vì bạn đang viết mã quản lý thư mục).

Xem tài liệu API OS của bạn để biết cách thực hiện việc này. Nếu bạn cần phải di động, bạn sẽ phải có một bó của #ifdef s cho các hệ điều hành khác nhau.

9

Ngoài hệ thống tệp tăng :: đã đề cập ở trên, bạn có thể muốn kiểm tra wxWidgets::wxDirQt::QDir.

Cả wxWidgets và Qt đều là các khung công tác nền tảng C++ dựa trên mã nguồn mở.

wxDir cung cấp cách linh hoạt để truyền tệp theo cách đệ quy bằng cách sử dụng Traverse() hoặc hàm GetAllFiles() đơn giản hơn.Bạn cũng có thể thực hiện traversal với các hàm GetFirst()GetNext() (tôi giả định rằng Traverse() và GetAllFiles() là các trình bao bọc mà cuối cùng sử dụng các hàm GetFirst() và GetNext()).

QDir cung cấp quyền truy cập vào cấu trúc thư mục và nội dung của chúng. Có nhiều cách để duyệt các thư mục với QDir. Bạn có thể lặp qua các nội dung thư mục (bao gồm các thư mục con) với QDirIterator đã được khởi tạo với cờ QDirIterator :: Subdirectories. Một cách khác là sử dụng hàm GetEntryList() của QDir và thực hiện một quá trình truyền đệ quy.

Đây là mã mẫu (được lấy từ here # Ví dụ 8-5) cho biết cách lặp qua tất cả các thư mục con.

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

Doxygen sử dụng QT làm lớp tương thích với hệ điều hành của nó. Các công cụ cốt lõi không sử dụng GUI ở tất cả các công cụ thư mục (và các công cụ khác). –

21

Giải pháp nhanh đang sử dụng thư viện Dirent.h của C.

đang làm việc đoạn từ 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; 
} 
+3

Thường trình này không đệ quy. – user501138

+0

Lưu ý tất cả: không hoạt động trên VC++. –

+0

@TimCooper, tất nhiên là không, dirent là posix cụ thể. – Vorac

30

Bạn có thể làm cho nó thậm chí còn đơn giản hơn với C++11 dòng sản phẩm mới dựa 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; 
} 
3

Bạn có thể sử dụng ftw(3) or nftw(3) đi bộ một hệ thống phân cấp hệ thống tập tin trong C hoặc C++ trên POSIX hệ thống.

+0

https://github.com/six-k/dtreetrawl/blob/f7c1d320225ee754b96fef28bb0774a2c34b91b8/dtreetrawl.c#L473 có ví dụ về điều này. Mã này thực hiện thêm một vài thứ nữa, nhưng nó hoạt động một hướng dẫn tốt để sử dụng 'nftw()'. –

3

Boost :: hệ thống tập tin cung cấp recursive_directory_iterator, mà là khá thuận tiện cho công việc này:

#include "boost/filesystem.hpp" 
#include <iostream> 

boost::filesystem::recursive_directory_iterator end; 
for (it("./"); it != end; ++it) { 
    std::cout << *it << std::endl;          
} 
1

Nếu bạn đang trên Windows, bạn có thể sử dụng cùng với FindFirstFile FindNextFile API. Bạn có thể sử dụng FindFileData.dwFileAttributes để kiểm tra xem một đường dẫn cụ thể có phải là một tệp hoặc một thư mục hay không. Nếu đó là một thư mục, bạn có thể lặp lại thuật toán một cách đệ quy.

Ở đây, tôi đã đặt cùng một số mã liệt kê tất cả các tệp trên máy tính Windows.

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

16

Trong C++ 11/14 với "Filesystem TS", các <experimental/filesystem> header và range- for bạn chỉ có thể làm điều này:

#include <experimental/filesystem> 

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

Tránh sử dụng 'using', sử dụng' namespace' để thay thế. –

+0

Và tại sao vậy? Tốt hơn cụ thể hơn là đưa vào những thứ bạn không sử dụng. –

+0

Xem lại chỉnh sửa của tôi, tôi cũng đã thêm không gian tên bị thiếu. –

2

Bạn có lẽ sẽ là tốt nhất với một trong hai boost hoặc C++ 14 của hệ thống tập tin thử nghiệm. NẾU bạn đang phân tích cú pháp một thư mục nội bộ (ví dụ: được sử dụng cho chương trình của bạn để lưu trữ dữ liệu sau khi chương trình đã đóng), sau đó tạo tệp chỉ mục có chỉ mục nội dung tệp. Bằng cách này, bạn có thể sẽ cần phải sử dụng tăng trong tương lai, vì vậy nếu bạn không có nó được cài đặt, cài đặt nó! Thứ hai, bạn có thể sử dụng một trình biên dịch có điều kiện:

#ifdef WINDOWS //define WINDOWS in your code to compile for windows 

mã tại 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. 
Các vấn đề liên quan