標準の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>
ヘッダー、および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;
C ++ 17以降std::filesystem
、標準ライブラリの一部であり、<filesystem>
ヘッダーにあります(「実験的」ではなくなりました)。
using
、namespace
代わりに使用してください。
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;
}
新しい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;
}
高速な解決策は、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 :: filesystemに加えて、wxWidgets :: wxDirおよびQt :: QDirを確認することもできます。
wxWidgetsとQtはどちらも、オープンソースのクロスプラットフォームC ++フレームワークです。
wxDir
は、再帰的にファイルをトラバースする柔軟な方法、Traverse()
またはより単純なGetAllFiles()
関数を提供します。同様に、GetFirst()
およびGetNext()
関数を使用してトラバーサルを実装できます(Traverse()およびGetAllFiles()は、最終的にGetFirst()およびGetNext()関数を使用するラッパーであると想定しています)。
QDir
ディレクトリ構造とその内容へのアクセスを提供します。QDirでディレクトリをトラバースする方法はいくつかあります。QDirIterator :: Subdirectoriesフラグでインスタンス化されたQDirIteratorを使用して、ディレクトリの内容(サブディレクトリを含む)を反復処理できます。別の方法は、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)
を使用して、POSIXシステム上のCまたはC ++でファイルシステム階層をウォークできます。
nftw()
使用するための優れたチュートリアルとして機能します。
おそらく、boostまたはc ++ 14の実験的なファイルシステムなどが最適です。IFあなたは(プログラムを閉じた後にデータを保存するためにあなたのプログラムのために使用さすなわち。)内部ディレクトリを解析している、そのファイルの内容のインデックスを持つインデックスファイルを作成します。ちなみに、将来的にはboostを使用する必要があるので、インストールしていない場合はインストールしてください!次に、条件付きコンパイルを使用できます。例:
#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()
およびのようなファイルシステムトラバーサルのためにOS固有の関数を呼び出す必要がありreaddir()
ます。C標準では、ファイルシステム関連の関数は指定されていません。
私たちは2019年にいますC++
。ファイルシステム標準ライブラリはにあります。Filesystem library
は、パス、通常のファイル、ディレクトリなど、ファイルシステムとそのコンポーネントで操作を実行するための機能を提供します。
移植性の問題を検討している場合は、このリンクに重要な注意事項があります。それは言う:
階層ファイルシステムが実装にアクセスできない場合、または必要な機能が提供されていない場合、ファイルシステムライブラリ機能は使用できない場合があります。基になるファイルシステムでサポートされていない機能は使用できない場合があります(FATファイルシステムにシンボリックリンクがなく、複数のハードリンクが禁止されているなど)。そのような場合、エラーを報告する必要があります。
ファイルシステムライブラリはもともととして開発boost.filesystem
され、技術仕様ISO / IEC TS 18822:2015として公開され、ついにC ++ 17の時点でISO C ++にマージされました。現在、ブースト実装は、C ++ 17ライブラリよりも多くのコンパイラとプラットフォームで利用できます。
@ adi-shavitがstd :: experimentalの一部であったときにこの質問に回答し、2017年にこの回答を更新しました。ライブラリの詳細と、より詳細な例を示したいと思います。
std :: filesystem :: recursive_directory_iteratorはLegacyInputIterator
、ディレクトリのdirectory_entry要素を反復し、再帰的にすべてのサブディレクトリのエントリを反復します。反復順序は指定されていませんが、各ディレクトリエントリは1回だけアクセスされます。
サブディレクトリのエントリを再帰的に反復したくない場合は、directory_iteratorを使用する必要があります。
どちらのイテレータも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;
}
Windowsを使用している場合は、FindFirstFileをFindNextFile APIと一緒に使用できます。FindFileData.dwFileAttributesを使用して、指定されたパスがファイルまたはディレクトリかどうかを確認できます。ディレクトリの場合は、アルゴリズムを再帰的に繰り返すことができます。
ここでは、Windowsマシン上のすべてのファイルをリストするいくつかのコードをまとめました。
ファイルツリーウォーク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;
}