كيف يمكنك التكرار خلال كل ملف/دليل بشكل متكرر في معيار C++؟

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

  •  09-06-2019
  •  | 
  •  

سؤال

كيف يمكنك التكرار خلال كل ملف/دليل بشكل متكرر في معيار C++؟

هل كانت مفيدة؟

المحلول

في معيار 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;
}

نصائح أخرى

إذا كنت تستخدم Win32 API، فيمكنك استخدام FindFirstFile و FindNextFile المهام.

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

يمكنك جعل الأمر أسهل مع الجديد سي++11 على أساس النطاق for و يعزز:

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

في C++ 11/14 مع "Filesystem TS"، تم تثبيت <experimental/filesystem> الرأس والمدى-for يمكنك ببساطة القيام بذلك:

#include <experimental/filesystem>

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

اعتبارًا من C++ 17، std::filesystem هو جزء من المكتبة القياسية ويمكن العثور عليه في <filesystem> الرأس (لم يعد "تجريبيًا").

الحل السريع هو استخدام لغة C Dirent.h مكتبة.

جزء كود العمل من ويكيبيديا:

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

بالإضافة إلى نظام الملفات Boost::MS المذكور أعلاه، قد ترغب في فحصه wxWidgets::wxDir و كيو تي::QDir.

يعد كل من wxWidgets وQt بمثابة أطر عمل مفتوحة المصدر ومتعددة الأنظمة الأساسية لـ C++.

wxDir يوفر طريقة مرنة لاجتياز الملفات بشكل متكرر باستخدام Traverse() أو أبسط GetAllFiles() وظيفة.كذلك يمكنك تنفيذ الاجتياز مع GetFirst() و GetNext() الوظائف (أفترض أن Traverse() وGetAllFiles() عبارة عن أغلفة تستخدم في النهاية وظائف GetFirst() وGetNext()).

QDir يوفر الوصول إلى هياكل الدليل ومحتوياتها.هناك عدة طرق لاجتياز الدلائل باستخدام QDir.يمكنك التكرار على محتويات الدليل (بما في ذلك الدلائل الفرعية) باستخدام QDirIterator الذي تم إنشاء مثيل له باستخدام علامة QDirIterator::Subdirectories.هناك طريقة أخرى وهي استخدام وظيفة GetEntryList() الخاصة بـ QDir وتنفيذ اجتياز عودي.

إليك نموذج التعليمات البرمجية (مأخوذ من هنا # المثال 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;
}

يوفر نظام 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;                                    
}

يمكنك استخدام ftw(3) أو nftw(3) للسير في التسلسل الهرمي لنظام الملفات في C أو C++ بوسيكس أنظمة.

لم تكن.لا يحتوي معيار C++ على مفهوم الدلائل.الأمر متروك للتنفيذ لتحويل السلسلة إلى مؤشر ملف.تعتمد محتويات تلك السلسلة وما تعينه على نظام التشغيل.ضع في اعتبارك أنه يمكن استخدام C++ لكتابة نظام التشغيل هذا، لذلك يتم استخدامه على المستوى الذي لم يتم فيه بعد تحديد كيفية التكرار عبر الدليل (لأنك تكتب رمز إدارة الدليل).

انظر إلى وثائق OS API الخاصة بك لمعرفة كيفية القيام بذلك.إذا كنت بحاجة إلى أن تكون محمولاً، فيجب أن يكون لديك مجموعة من #ifdefلأنظمة تشغيل مختلفة.

من المحتمل أن تكون أفضل مع نظام الملفات التجريبي Boost أو 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.

تحتاج إلى استدعاء الوظائف الخاصة بنظام التشغيل لاجتياز نظام الملفات، مثل open() و readdir().لا يحدد معيار C أي وظائف متعلقة بنظام الملفات.

لم تكن.لا يعرض معيار C++ مفهوم الدليل.على وجه التحديد، لا يوفر أي طريقة لسرد جميع الملفات الموجودة في الدليل.

قد يكون الاختراق الرهيب هو استخدام مكالمات النظام () وتحليل النتائج.سيكون الحل الأكثر منطقية هو استخدام نوع من المكتبات المشتركة بين الأنظمة الأساسية مثل كيو تي او حتى بوسيكس.

نحن في عام 2019.لدينا نظام الملفات المكتبة القياسية في C++.ال Filesystem library يوفر تسهيلات لتنفيذ العمليات على أنظمة الملفات ومكوناتها، مثل المسارات والملفات العادية والدلائل.

هناك ملاحظة مهمة بشأن هذا الرابط إذا كنت تفكر في مشكلات قابلية النقل.انها تقول:

قد تكون مرافق مكتبة نظام الملفات غير متاحة إذا لم يكن من الممكن الوصول إلى نظام الملفات الهرمي للتنفيذ، أو إذا كان لا يوفر الإمكانيات اللازمة.قد لا تكون بعض الميزات متاحة إذا لم تكن مدعومة من قبل نظام الملفات الأساسي (على سبيل المثال.يفتقر نظام الملفات FAT إلى الروابط الرمزية ويمنع الارتباطات الثابتة المتعددة).وفي هذه الحالات، يجب الإبلاغ عن الأخطاء.

تم تطوير مكتبة نظام الملفات في الأصل باسم boost.filesystem, ، تم نشرها كمواصفة فنية ISO/IEC TS 18822:2015، وتم دمجها أخيرًا في ISO C++ اعتبارًا من C++17.يتوفر تنفيذ التعزيز حاليًا على عدد أكبر من المترجمين والأنظمة الأساسية مقارنة بمكتبة C++ 17.

لقد أجاب @adi-shavit على هذا السؤال عندما كان جزءًا من std::experimental وقام بتحديث هذه الإجابة في عام 2017.أريد تقديم المزيد من التفاصيل حول المكتبة وإظهار مثال أكثر تفصيلاً.

std::filesystem::recursive_directory_iterator هو LegacyInputIterator الذي يتكرر عبر عناصر Directory_entry الخاصة بالدليل، وبشكل متكرر، عبر إدخالات كافة الدلائل الفرعية.ترتيب التكرار غير محدد، فيما عدا أنه تتم زيارة كل إدخال دليل مرة واحدة فقط.

إذا كنت لا ترغب في التكرار بشكل متكرر على إدخالات الدلائل الفرعية، إذن Directory_iterator يجب أن تستخدم.

يقوم كلا المكررين بإرجاع كائن Directory_entry. directory_entry لديه العديد من وظائف الأعضاء المفيدة مثل is_regular_file, is_directory, is_socket, is_symlink إلخ.ال path() تقوم دالة العضو بإرجاع كائن من الأمراض المنقولة جنسيا :: نظام الملفات :: المسار ويمكن استخدامه للحصول على file extension, filename, root name.

النظر في المثال أدناه.لقد كنت استخدم Ubuntu وتجميعها عبر المحطة باستخدام

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

إذا كنت تستخدم نظام التشغيل Windows، فيمكنك استخدام FindFirstFile مع FindNextFile API.يمكنك استخدام FindFileData.dwFileAttributes للتحقق مما إذا كان المسار المحدد عبارة عن ملف أو دليل.إذا كان دليلاً، فيمكنك تكرار الخوارزمية بشكل متكرر.

لقد قمت هنا بتجميع بعض التعليمات البرمجية التي تسرد جميع الملفات الموجودة على جهاز يعمل بنظام Windows.

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top