it-swarm-id.com

Bagaimana Anda mengulangi setiap file / direktori secara rekursif dalam standar C ++?

Bagaimana Anda mengulangi setiap file/direktori secara rekursif dalam standar C++?

102
robottobor

Dalam standar C++, secara teknis tidak ada cara untuk melakukan ini karena standar C++ tidak memiliki konsep direktori. Jika Anda ingin sedikit memperluas jaringan Anda, Anda mungkin ingin melihat menggunakan Boost.FileSystem . Ini telah diterima untuk dimasukkan dalam TR2, jadi ini memberi Anda peluang terbaik untuk menjaga implementasi Anda sedekat mungkin dengan standar.

Contoh, diambil langsung dari situs 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;
}
94
1800 INFORMATION

Jika menggunakan Win32 API Anda dapat menggunakan FindFirstFile dan FindNextFile fungsi.

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

Untuk traversal direktori rekursif Anda harus memeriksa masing-masing WIN32_FIND_DATA.dwFileAttributes untuk memeriksa apakah FILE_ATTRIBUTE_DIRECTORY bit diatur. Jika bit sudah diatur maka Anda dapat memanggil fungsi dengan direktori itu secara rekursif. Atau Anda dapat menggunakan stack untuk memberikan efek yang sama dari panggilan rekursif tetapi menghindari stack overflow untuk pohon path yang sangat panjang.

#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

Dengan C++ 17, <filesystem> tajuk, dan rentang -for, Anda dapat melakukan ini:

#include <filesystem>

using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
...
for (const auto& dirEntry : recursive_directory_iterator(myPath))
     std::cout << dirEntry << std::endl;

Pada C++ 17, std::filesystem adalah bagian dari perpustakaan standar dan dapat ditemukan di <filesystem> tajuk (tidak lagi "percobaan").

36
Adi Shavit

Anda dapat membuatnya lebih sederhana dengan --- C++ 11 berbasis rentang for dan Boost :

#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

Solusi cepat menggunakan pustaka C Dirent.h .

Fragmen kode kerja dari 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

Selain boost :: filesystem yang disebutkan di atas, Anda mungkin ingin memeriksa wxWidgets :: wxDir dan Qt :: QDir .

Baik wxWidgets dan Qt adalah open source, cross platform C++ frameworks.

wxDir menyediakan cara yang fleksibel untuk menelusuri file secara rekursif menggunakan Traverse() atau fungsi GetAllFiles() yang lebih sederhana. Anda juga dapat mengimplementasikan traversal dengan fungsi GetFirst() dan GetNext() (saya berasumsi bahwa Traverse () dan GetAllFiles () adalah pembungkus yang akhirnya menggunakan fungsi GetFirst () dan GetNext ()).

QDir menyediakan akses ke struktur direktori dan isinya. Ada beberapa cara untuk melintasi direktori dengan QDir. Anda dapat mengulangi isi direktori (termasuk sub-direktori) dengan QDirIterator yang dibuat dengan QDirIterator :: flag Subdirectories. Cara lain adalah dengan menggunakan fungsi GetEntryList () QDir dan mengimplementasikan traversal rekursif.

Berikut adalah contoh kode (diambil dari di sini # Contoh 8-5) yang menunjukkan cara mengulangi semua sub direktori.

#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 menyediakan recursive_directory_iterator, yang cukup nyaman untuk tugas ini:

#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

Anda dapat menggunakan ftw(3) atau nftw(3) untuk menjalankan hierarki sistem file di C atau C++ pada POSIX sistem.

4
leif

Anda mungkin akan menjadi yang terbaik dengan sistem file eksperimental boost atau c ++ 14. [~ # ~] jika [~ # ~] Anda menguraikan direktori internal (mis. digunakan untuk program Anda untuk menyimpan data setelah program ditutup ), lalu buat file indeks yang memiliki indeks isi file. Ngomong-ngomong, Anda mungkin perlu menggunakan boost di masa depan, jadi jika Anda belum menginstalnya, instal! Kedua, Anda dapat menggunakan kompilasi bersyarat, mis .:

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

Kode untuk setiap kasus diambil dari 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

Kamu tidak. Standar C++ tidak memiliki konsep direktori. Terserah implementasi untuk mengubah string menjadi pegangan file. Isi string itu dan apa yang dipetakannya bergantung pada OS. Perlu diingat bahwa C++ dapat digunakan untuk menulis OS itu, sehingga akan digunakan pada tingkat di mana menanyakan bagaimana cara mengulangi melalui direktori belum ditentukan (karena Anda sedang menulis kode manajemen direktori).

Lihatlah dokumentasi API OS Anda untuk cara melakukan ini. Jika Anda perlu portabel, Anda harus memiliki banyak # ifdef s untuk berbagai OS.

3
Matthew Scouten

Anda perlu memanggil fungsi spesifik OS untuk traversal sistem file, seperti open() dan readdir(). Standar C tidak menentukan fungsi apa pun yang terkait dengan sistem file.

2
John Millikin

Kamu tidak. Standar C++ tidak memaparkan konsep direktori. Khususnya itu tidak memberikan cara apa pun untuk mendaftar semua file dalam direktori.

Peretasan yang mengerikan akan menggunakan system () panggilan dan mengurai hasilnya. Solusi yang paling masuk akal adalah dengan menggunakan beberapa jenis pustaka lintas platform seperti Qt atau bahkan POSIX .

1
shoosh

Kami berada di 2019. Kami memiliki filesystem perpustakaan standar di C++. Filesystem library Menyediakan fasilitas untuk melakukan operasi pada sistem file dan komponennya, seperti jalur, file biasa, dan direktori.

Ada catatan penting pada tautan ini jika Anda mempertimbangkan masalah portabilitas. Ia mengatakan:

Fasilitas pustaka sistem file mungkin tidak tersedia jika sistem file hierarkis tidak dapat diakses untuk implementasi, atau jika tidak menyediakan kemampuan yang diperlukan. Beberapa fitur mungkin tidak tersedia jika tidak didukung oleh sistem file yang mendasarinya (mis. Sistem file FAT tidak memiliki tautan simbolis dan melarang beberapa hardlink). Dalam kasus tersebut, kesalahan harus dilaporkan.

Pustaka filesystem awalnya dikembangkan sebagai boost.filesystem, Diterbitkan sebagai spesifikasi teknis ISO/IEC TS 18822: 2015, dan akhirnya digabungkan ke ISO C++ pada C++ 17. Implementasi boost saat ini tersedia pada lebih banyak kompiler dan platform dibandingkan pustaka C++ 17.

@ adi-shavit telah menjawab pertanyaan ini ketika itu adalah bagian dari std :: eksperimental dan ia telah memperbarui jawaban ini pada tahun 2017. Saya ingin memberikan rincian lebih lanjut tentang perpustakaan dan menunjukkan contoh yang lebih rinci.

std :: filesystem :: recursive_directory_iterator adalah LegacyInputIterator yang beralih ke elemen directory_entry dari sebuah direktori, dan, secara rekursif, atas entri semua subdirektori. Urutan iterasi tidak ditentukan, kecuali bahwa setiap entri direktori dikunjungi hanya sekali.

Jika Anda tidak ingin secara berulang mengulangi entri subdirektori, maka directory_iterator harus digunakan.

Kedua iterators mengembalikan objek directory_entry . directory_entry Memiliki berbagai fungsi anggota yang berguna seperti is_regular_file, is_directory, is_socket, is_symlink Dll. path() anggota function mengembalikan objek std :: filesystem :: path dan itu dapat digunakan untuk mendapatkan file extension, filename, root name.

Perhatikan contoh di bawah ini. Saya telah menggunakan Ubuntu dan mengkompilasinya menggunakan terminal

g ++ example.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;
}
0
abhiarora