Как вы повторяете каждый каталог/каталог рекурсивно в стандартном С++?
Как вы повторяете каждый каталог/каталог рекурсивно в стандартном С++?
Ответ 1
В стандартном С++ технически нет никакого способа сделать это, поскольку стандартный С++ не имеет представления о каталогах. Если вы хотите немного расширить свою сеть, вам может понравиться использовать 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;
}
Ответ 2
Если вы используете API 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;
}
Ответ 3
Вы можете сделать это еще проще с новым C++11 на основе for
и 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;
}
Ответ 4
С С++ 17, заголовком <filesystem>
и range- 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;
Начиная с С++ 17, std::filesystem
является частью стандартной библиотеки и находится в заголовке <filesystem>
(больше не "экспериментальный").
Ответ 5
Быстрое решение использует библиотеку 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;
}
Ответ 6
В дополнение к вышеупомянутой boost:: filesystem вы можете изучить wxWidgets:: wxDir и Qt:: QDir.
Оба wxWidgets и Qt представляют собой открытые исходные, межплатформенные платформы С++.
wxDir
обеспечивает гибкий способ рекурсивно перемещать файлы с помощью Traverse()
или более простой GetAllFiles()
функции. Кроме того, вы можете реализовать обход с функциями GetFirst()
и GetNext()
(я полагаю, что 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;
}
Ответ 7
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;
}
Ответ 8
Возможно, вы захотите изучить boost.filesystem
http://www.boost.org/doc/libs/1_31_0/libs/filesystem/doc/index.htm
Ответ 9
Вы можете использовать ftw(3)
или nftw(3)
для перехода иерархии файловой системы на C или С++ на POSIX.
Ответ 10
Нет. Стандарт С++ не имеет понятия каталогов. Реализация должна превратить строку в дескриптор файла. Содержимое этой строки и то, что она сопоставляет, зависит от ОС. Имейте в виду, что С++ можно использовать для записи этой ОС, поэтому он используется на уровне, где запрос о том, как выполнять итерацию по каталогу, еще не определен (потому что вы пишете код управления каталогом).
Посмотрите на документацию по API-интерфейсу ОС для того, как это сделать. Если вам нужно быть портативным, вам понадобится куча # ifdef для различных ОС.
Ответ 11
Вы, вероятно, лучше всего подойдете для экспериментальных файловых систем boost или С++ 14. Если вы анализируете внутренний каталог (т.е. используемый для вашей программы для хранения данных после закрытия программы), то создайте индексный файл, который имеет индекс содержимого файла. Кстати, вам, вероятно, понадобится использовать boost в будущем, поэтому, если он не установлен, установите его! Во-вторых, вы можете использовать условную компиляцию, например:
#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif
Код для каждого случая взят из fooobar.com/questions/65017/...
#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.
Ответ 12
Вам нужно вызвать функции, специфичные для ОС, для обхода файловой системы, например open()
и readdir()
. В стандарте C не указаны функции, связанные с файловой системой.
Ответ 13
Нет. Стандартный С++ не раскрывает концепции каталога. В частности, это не дает возможности перечислить все файлы в каталоге.
Ужасным взломом было бы использовать вызовы system() и анализировать результаты. Наиболее разумным решением было бы использовать какую-то кросс-платформенную библиотеку, такую как Qt или даже POSIX.
Ответ 14
Мы находимся в 2019 году. У нас есть стандартная библиотека файловой системы в C++
. Библиотека Filesystem library
предоставляет средства для выполнения операций с файловыми системами и их компонентами, такими как пути, обычные файлы и каталоги.
На этой ссылке есть важное примечание, если вы рассматриваете проблемы переносимости. Это говорит:
Средства библиотеки файловой системы могут быть недоступны, если иерархическая файловая система недоступна для реализации или если она не обеспечивает необходимые возможности. Некоторые функции могут быть недоступны, если они не поддерживаются базовой файловой системой (например, файловая система FAT не имеет символических ссылок и запрещает множественные жесткие ссылки). В этих случаях необходимо сообщать об ошибках.
Библиотека файловой системы изначально была разработана как boost.filesystem
, была опубликована в виде технической спецификации ISO/IEC TS 18822: 2015 и, наконец, объединена с ISO C++ с C++ 17. Реализация boost в настоящее время доступна на большем количестве компиляторов и платформ, чем библиотека C++ 17.
@adi-shavit ответил на этот вопрос, когда он был частью std :: экспериментальный, и он обновил этот ответ в 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()
возвращает объект std :: filesystem :: path и может использоваться для получения file extension
filename
, root name
filename
, root name
.
Рассмотрим пример ниже. Я использовал Ubuntu
и скомпилировал его через терминал, используя
g++ example.cpp --std = C++ 17-й C++ 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;
}
Ответ 15
Если вы находитесь в Windows, вы можете использовать FindFirstFile вместе с API FindNextFile. Вы можете использовать FindFileData.dwFileAttributes, чтобы проверить, является ли данный путь файлом или каталогом. Если это каталог, вы можете рекурсивно повторять алгоритм.
Здесь я собрал код, в котором перечислены все файлы на компьютере Windows.