आप हर फ़ाइल / निर्देशिका के माध्यम से पुनरावृत्ति कैसे मानक C ++ में करते हैं?
आप हर फ़ाइल / निर्देशिका के माध्यम से पुनरावृत्ति कैसे मानक 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;
}
C ++ 17 से आगे, <filesystem>
हेडर और रेंज- से for
, आप बस ऐसा कर सकते हैं:
#include <filesystem>
using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
...
for (const auto& dirEntry : recursive_directory_iterator(myPath))
std::cout << dirEntry << std::endl;
C ++ 17 के std::filesystem
रूप में, मानक लाइब्रेरी का हिस्सा है और <filesystem>
हेडर में पाया जा सकता है (अब "प्रयोगात्मक")।
using
, उपयोग namespace
के बजाय।
Win32 एपीआई का उपयोग कर यदि आप उपयोग कर सकते हैं 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;
}
आप इसे नई C ++ 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;
}
एक तेजी से समाधान सी के डिरेंट.एच लाइब्रेरी का उपयोग कर रहा है ।
विकिपीडिया से कार्य कोड का टुकड़ा:
#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;
}
उपर्युक्त बढ़ावा के अलावा :: फाइलसिस्टम आप wxWidgets की जांच करना चाह सकते हैं :: wxDir और Qt :: QDIR ।
WxWidgets और Qt दोनों ओपन सोर्स हैं, क्रॉस प्लेटफॉर्म C ++ चौखटे हैं।
wxDir
पुनरावृत्ति का उपयोग करके Traverse()
या एक सरल GetAllFiles()
कार्य के लिए फ़ाइलों को पार करने के लिए एक लचीला तरीका प्रदान करता है । साथ ही आप ट्रैवर्सल को फ़ंक्शंस GetFirst()
और GetNext()
फ़ंक्शंस के साथ लागू कर सकते हैं (मेरा मानना है कि ट्रैवर्स () और गेटअलीफ़ाइल्स () ऐसे रैपर हैं जो अंततः गेटफ़र्स्ट () और गेटनेक्स्ट () फ़ंक्शन का उपयोग करते हैं।
QDir
निर्देशिका संरचनाओं और उनकी सामग्री तक पहुँच प्रदान करता है। QDir के साथ निर्देशिकाओं को पार करने के कई तरीके हैं। आप QDirIterator के साथ निर्देशिका सामग्रियों (उप-निर्देशिकाओं सहित) पर पुन: प्रसारित कर सकते हैं जो QDirIterator :: Subdirectories ध्वज के साथ त्वरित किया गया था। एक और तरीका है QDir के GetEntryList () फ़ंक्शन का उपयोग करना और एक पुनरावर्ती ट्रैवर्स को लागू करना।
यहाँ नमूना कोड है ( यहाँ से लिया गया उदाहरण # 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;
}
बूस्ट :: फाइलसिस्टम 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;
}
आप POSIX सिस्टम पर C या C ++ में फ़ाइल सिस्टम पदानुक्रम का उपयोग कर सकते हैं ftw(3)
याnftw(3)
उसका उपयोग कर सकते हैं ।
nftw()
उपयोग के लिए एक अच्छा ट्यूटोरियल कार्य करता है ।
तुम नहीं। C ++ मानक में निर्देशिकाओं की कोई अवधारणा नहीं है। यह एक स्ट्रिंग को फ़ाइल हैंडल में बदलने के लिए लागू करना है। उस स्ट्रिंग की सामग्री और जो यह मैप करता है वह ओएस पर निर्भर है। ध्यान रखें कि C ++ का उपयोग उस OS को लिखने के लिए किया जा सकता है, इसलिए इसका उपयोग ऐसे स्तर पर किया जाता है, जहां यह पूछना कि किसी निर्देशिका के माध्यम से पुनरावृति करना अभी तक परिभाषित नहीं किया गया है (क्योंकि आप निर्देशिका प्रबंधन कोड लिख रहे हैं)।
ऐसा करने के लिए अपने ओएस एपीआई प्रलेखन को देखें। यदि आपको पोर्टेबल होने की आवश्यकता है, तो आपको विभिन्न OSes के लिए #ifdef s का एक गुच्छा रखना होगा ।
आप शायद बूस्ट या सी ++ 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()
। सी मानक किसी भी फाइलसिस्टम से संबंधित कार्यों को निर्दिष्ट नहीं करता है।
हम 2019 में हैं। हमारे पास फाइलसिस्टम मानक पुस्तकालय है C++
। Filesystem library
इस तरह के रास्तों, नियमित रूप से फाइलें, और निर्देशिका के रूप में फाइल सिस्टम और उनके घटक है, पर ऑपरेशनों को करने के लिए सुविधाएं प्रदान करता है।
इस लिंक पर एक महत्वपूर्ण नोट है यदि आप पोर्टेबिलिटी के मुद्दों पर विचार कर रहे हैं। इसे कहते हैं:
यदि एक पदानुक्रमित फ़ाइल सिस्टम कार्यान्वयन के लिए सुलभ नहीं है, या यदि यह आवश्यक क्षमताओं को प्रदान नहीं करता है, तो फाइल सिस्टम लाइब्रेरी की सुविधा अनुपलब्ध हो सकती है। कुछ सुविधाएँ उपलब्ध नहीं हो सकती हैं यदि वे अंतर्निहित फ़ाइल सिस्टम द्वारा समर्थित नहीं हैं (उदाहरण के लिए FAT फाइल सिस्टम में प्रतीकात्मक लिंक का अभाव है और कई हार्डलिंक को मना करता है)। उन मामलों में, त्रुटियों की सूचना दी जानी चाहिए।
फाइलसिस्टम लाइब्रेरी को मूल रूप से विकसित किया boost.filesystem
गया था, तकनीकी विनिर्देश आईएसओ / आईईसी टीएस 18822: 2015 के रूप में प्रकाशित किया गया था, और अंत में सी ++ 17 के रूप में आईएसओ सी ++ में विलय कर दिया गया था। बूस्ट कार्यान्वयन वर्तमान में C ++ 17 पुस्तकालय की तुलना में अधिक संकलक और प्लेटफार्मों पर उपलब्ध है।
@ adi-shavit ने इस सवाल का जवाब दिया है जब यह std :: प्रयोगात्मक का हिस्सा था और उन्होंने 2017 में इस उत्तर को अपडेट किया है। मैं पुस्तकालय के बारे में अधिक विवरण देना चाहता हूं और अधिक विस्तृत उदाहरण दिखाना चाहता हूं।
std :: फाइल सिस्टम :: recursive_directory_iterator है एक LegacyInputIterator
है कि एक निर्देशिका के directory_entry तत्वों, और, रिकर्सिवली, सभी सबडायरेक्टरियों की प्रविष्टियों से अधिक से अधिक दोहराता। पुनरावृति क्रम अनिर्दिष्ट है, सिवाय इसके कि प्रत्येक निर्देशिका प्रविष्टि केवल एक बार देखी जाती है।
यदि आप उपनिर्देशिकाओं की प्रविष्टियों पर पुनरावृत्ति नहीं करना चाहते हैं, तो directory_iterator का उपयोग किया जाना चाहिए।
दोनों iterators की एक वस्तु देता है directory_entry । directory_entry
जैसे विभिन्न उपयोगी सदस्य कार्य करता है is_regular_file
, is_directory
, is_socket
, is_symlink
आदि path()
सदस्य समारोह रिटर्न की एक वस्तु std :: फाइल सिस्टम :: पथ और इसे पाने के लिए इस्तेमाल किया जा सकता 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;
}
तुम नहीं। मानक सी ++ एक निर्देशिका की अवधारणा को उजागर नहीं करता है। विशेष रूप से यह किसी निर्देशिका में सभी फ़ाइलों को सूचीबद्ध करने का कोई तरीका नहीं देता है।
एक भयानक हैक प्रणाली () कॉल का उपयोग करने और परिणामों को पार्स करने के लिए होगा। सबसे उचित समाधान कुछ प्रकार के क्रॉस-प्लेटफॉर्म लाइब्रेरी जैसे कि क्यूटी या यहां तक कि पॉसिक्स का उपयोग करना होगा ।
आप उपयोग कर सकते हैं std::filesystem::recursive_directory_iterator
। लेकिन सावधान रहें इसमें प्रतीकात्मक (नरम) लिंक शामिल हैं। यदि आप उनसे बचना चाहते हैं तो आप उपयोग कर सकते हैं is_symlink
। उदाहरण उपयोग:
size_t directorySize(const std::filesystem::path& directory)
{
size_t size{ 0 };
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory))
{
if (entry.is_regular_file() && !entry.is_symlink())
{
size += entry.file_size();
}
}
return size;
}
यदि आप विंडोज पर हैं, तो आप फाइंडनस्टाइल एपीआई के साथ फाइंडफ्रस्टाइल का उपयोग कर सकते हैं। आप दिए गए पथ फ़ाइल या निर्देशिका है या नहीं यह जानने के लिए FindFileData.dwFileAttributes का उपयोग कर सकते हैं। यदि यह एक निर्देशिका है, तो आप एल्गोरिथम को पुन: दोहरा सकते हैं।
यहां, मैंने कुछ कोड को एक साथ रखा है जो सभी फाइलों को विंडोज मशीन पर सूचीबद्ध करता है।
फ़ाइल ट्री वॉक ftw
पथ में पूरी निर्देशिका ट्री की दीवार को पुनरावर्ती करने के लिए एक पुनरावर्ती तरीका है। अधिक जानकारी यहाँ हैं ।
नोट: आप भी उपयोग कर सकते हैं fts
उस तरह छिपा फ़ाइलों को छोड़ सकते हैं .
या ..
या.bashrc
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
printf("0%3o\t%s\n", status->st_mode&0777, name);
}
if (type == FTW_D && strcmp(".", name) != 0)
{
printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}
आउटपुट निम्न जैसा दिखता है:
0755 ./Shivaji/
0644 ./Shivaji/20200516_204454.png
0644 ./Shivaji/20200527_160408.png
0644 ./Shivaji/20200527_160352.png
0644 ./Shivaji/20200520_174754.png
0644 ./Shivaji/20200520_180103.png
0755 ./Saif/
0644 ./Saif/Snapchat-1751229005.jpg
0644 ./Saif/Snapchat-1356123194.jpg
0644 ./Saif/Snapchat-613911286.jpg
0644 ./Saif/Snapchat-107742096.jpg
0755 ./Milind/
0644 ./Milind/IMG_1828.JPG
0644 ./Milind/IMG_1839.JPG
0644 ./Milind/IMG_1825.JPG
0644 ./Milind/IMG_1831.JPG
0644 ./Milind/IMG_1840.JPG
यदि आप *.jpg, *.jpeg, *.png
किसी विशिष्ट आवश्यकताओं के लिए फ़ाइल नाम (उदाहरण: सभी फ़ाइलों की खोज करना ) का उपयोग करना चाहते हैं, तो हमें बताएं fnmatch
।
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <fnmatch.h>
static const char *filters[] = {
"*.jpg", "*.jpeg", "*.png"
};
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
int i;
for (i = 0; i < sizeof(filters) / sizeof(filters[0]); i++) {
/* if the filename matches the filter, */
if (fnmatch(filters[i], name, FNM_CASEFOLD) == 0) {
printf("0%3o\t%s\n", status->st_mode&0777, name);
break;
}
}
}
if (type == FTW_D && strcmp(".", name) != 0)
{
//printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}