標準 C++ ですべてのファイル/ディレクトリを再帰的に繰り返すにはどうすればよいでしょうか?
-
09-06-2019 - |
質問
標準 C++ ですべてのファイル/ディレクトリを再帰的に繰り返すにはどうすればよいでしょうか?
解決
標準 C++ にはディレクトリの概念がないため、技術的にこれを行う方法はありません。ネットワークを少し拡張したい場合は、次の使用を検討するとよいでしょう。 ブーストファイルシステム. 。これは TR2 に含めることが認められているため、実装を可能な限り標準に近づける可能性が最も高くなります。
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;
}
他のヒント
Win32 API を使用している場合は、 最初のファイルを見つける そして 次のファイルを探す 機能。
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;
}
「ファイルシステム TS」を使用する C++11/14 では、 <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 を使用することです ディレント.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::filesystem に加えて、調べたい場合もあります。 wxWidgets::wxDir そして Qt::QDir.
wxWidgets と Qt はどちらもオープンソースのクロスプラットフォーム C++ フレームワークです。
wxDir
を使用してファイルを再帰的に走査する柔軟な方法を提供します。 Traverse()
またはもっと単純な GetAllFiles()
関数。同様に、トラバースを実装することもできます GetFirst()
そして GetNext()
関数 (Traverse() と GetAllFiles() は、最終的に GetFirst() 関数と GetNext() 関数を使用するラッパーであると仮定します)。
QDir
ディレクトリ構造とその内容へのアクセスを提供します。QDir を使用してディレクトリをトラバースする方法はいくつかあります。QDirIterator::Subdirectories フラグでインスタンス化された QDirIterator を使用して、ディレクトリの内容 (サブディレクトリを含む) を反復処理できます。もう 1 つの方法は、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;
}
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++ でファイルシステム階層をたどるには POSIX システム。
あなたはしない。C++ 標準にはディレクトリの概念がありません。文字列をファイル ハンドルに変換するかどうかは実装次第です。その文字列の内容とそれが何にマップされるかは、OS によって異なります。C++ はその OS の作成に使用できるため、ディレクトリを反復処理する方法がまだ定義されていないレベルで使用されることに注意してください (ディレクトリ管理コードを作成しているため)。
これを行う方法については、OS API ドキュメントを参照してください。持ち運びが必要な場合は、たくさんのものが必要になります #ifdefさまざまな OS に対応。
おそらく、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.
ファイルシステムのトラバーサルには、次のような OS 固有の関数を呼び出す必要があります。 open()
そして readdir()
. 。C 標準では、ファイルシステム関連の関数は指定されていません。
2019年になりました。我々は持っています ファイルシステム 標準ライブラリの C++
. 。の Filesystem library
ファイル システムとそのコンポーネント (パス、通常のファイル、ディレクトリなど) に対して操作を実行するための機能を提供します。
重要な注意事項があります このリンク 移植性の問題を考慮している場合。こう書かれています。
階層ファイル システムが実装にアクセスできない場合、または階層ファイル システムが必要な機能を提供していない場合、ファイル システム ライブラリ機能は利用できない場合があります。一部の機能は、基盤となるファイル システムでサポートされていない場合は利用できない場合があります (例:FAT ファイルシステムにはシンボリック リンクがなく、複数のハードリンクが禁止されています)。このような場合は、エラーを報告する必要があります。
ファイルシステム ライブラリは元々次のように開発されました。 boost.filesystem
, 、技術仕様 ISO/IEC TS 18822:2015 として公開され、最終的に C++17 で ISO C++ にマージされました。ブースト実装は現在、C++17 ライブラリよりも多くのコンパイラとプラットフォームで利用できます。
@adi-savit は、std::experimental の一部だったときにこの質問に回答し、2017 年にこの回答を更新しました。ライブラリについてさらに詳しく説明し、より詳細な例を示したいと思います。
std::filesystem::recursive_directory_iterator です LegacyInputIterator
これは、ディレクトリの directory_entry 要素を反復し、すべてのサブディレクトリのエントリを再帰的に反復します。各ディレクトリ エントリが 1 回だけアクセスされることを除いて、反復順序は指定されていません。
サブディレクトリのエントリを再帰的に反復したくない場合は、 ディレクトリイテレータ 使用すべきです。
両方の反復子は次のオブジェクトを返します。 ディレクトリエントリ. 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;
}
Windows を使用している場合は、FindFirstFile を FindNextFile API と一緒に使用できます。FindFileData.dwFileAttributes を使用すると、指定されたパスがファイルであるかディレクトリであるかを確認できます。ディレクトリの場合は、アルゴリズムを再帰的に繰り返すことができます。
ここでは、Windows マシン上のすべてのファイルをリストするコードをまとめました。