標準のC ++ですべてのファイル/ディレクトリを再帰的に反復するにはどうすればよいですか?


115

標準のC ++ですべてのファイル/ディレクトリを再帰的に反復するにはどうすればよいですか?


あなたはboost.filesystem調べたいことがありboost.org/doc/libs/1_31_0/libs/filesystem/doc/index.htmを
ダグT.


1
これは、recursive_directory_iteratorを使用して、ファイルシステムTSを介して標準にすぐに含まれるはずです
Adi Shavit

標準Cライブラリを使用してもC ++プログラムを「標準」として呼び出すことができない場合は、nftw()。これが実用的な例です
6-k '

2
彼らが何をしているかを知っている誰かがこれを更新するのに1時間かかるはずです。
Josh C

回答:


99

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

5
C ++にはファイルの概念はありませんか?std :: fstreamはどうですか?またはfopen?
ケビン

30
ディレクトリではなくファイル
1800情報

22
最新のブーストバージョンに関する更新:誰かがこの回答を見つけた場合、最新のブーストには便利なクラスboost :: recursive_directory_iteratorが含まれているため、明示的な再帰呼び出しで上記のループを記述する必要はありません。リンク: boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/...
JasDev

5
VC ++ 11は、std :: tr2 :: sys名前空間の<filesystem>ヘッダーにほとんど同じ機能を備えています。
mheyman 2013

3
これは良い答えでしたが、<filesystem>が標準になったので、単純に使うほうが良いです(例については他の答えを参照してください)。
Gathar

54

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>ヘッダーにあります(「実験的」ではなくなりました)。


の使用は避けusingnamespace代わりに使用してください。
Roi Danton 2017

2
なんで?使用しないものを持ち込むよりも具体的です。
Adi Shavit 2017

編集内容を確認してください。欠落しているネームスペースstdも追加しました。
Roi Danton 2017

5
<filesystem>はTSではなくなりました。C ++ 17の一部です。おそらくそれに応じてこの回答を更新する必要があります。
IInspectable 2017年

Macユーザーへの注意:これには少なくともOSX 10.15(Catalina)が必要です。
ジャスティン

45

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

19
それを書くのにどれくらい時間がかかりましたか?C ++をpythonに接着して1行で実行するのにかかる時間は短いと思います。
ダスティンゲッツ

2
これは、再帰的でない優れたソリューションです(便利な場合もあります)。
jm。

1
ところで、ハードコードされたパス( "F:\\ cvsrepos")ではなく、コマンドラインパラメータargv [1]を受け入れるようにプログラムを少し編集したい場合は、main(int、char)のシグネチャが変更されます次のようにwmain(int、wchar_t)に:int wmain(int argc、wchar_t * argv [])
JasDev

1
おかげで、この関数はCyrilicでは機能しません。-б、в、гなどのCyrilic文字で動作させる方法はありますか?
unresolved_external

31

新しいC ++ 11範囲ベースforBoostを使用すると、さらにシンプルにすることができます。

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

5
ブーストの必要はありません。OPは特に標準c ++を要求しました。
クレイグB

23

高速な解決策は、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;
}

5
このルーチンは再帰的ではありません。
user501138 2015

@TimCooper、もちろんそうではありません、direntはposix固有です。
Vorac

1
Tony RonkkoによるビジュアルC ++のdirent.hのポートを取得した場合、実際に VC ++で動作します。FOSSです。私はこれを試したところ、うまくいきました。
user1741137

10

上記の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;
}

DoxygenはQTをOS互換性レイヤーとして使用しています。コアツールは、ディレクトリのもの(および他のコンポーネント)だけでGUIをまったく使用しません。
deft_code

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

1
「それ」って何ですか?構文エラーはありませんか?そして、どのように「終わり」を養うのですか?(=どのようにしてすべてのディレクトリを解析したか?)
yO_

1
@yO_そうです、タイプミスがありました。recursive_directory_iteratorのデフォルトのコンストラクタは「無効な」イテレータを作成します。dirの反復が完了すると、「it」は無効になり、「end」と等しくなります
DikobrAz


4

あなたはしません。C ++標準には、ディレクトリの概念はありません。文字列をファイルハンドルに変換するのは実装次第です。その文字列の内容とその文字列のマッピング先はOSに依存します。C ++はそのOSの記述に使用できるため、ディレクトリを反復する方法を尋ねる方法がまだ定義されていないレベルで使用されることに注意してください(ディレクトリ管理コードを記述しているため)。

これを行う方法については、OS APIのドキュメントを参照してください。移植性が必要な場合は、さまざまなOSに対応する多数#ifdefを用意する必要があります。


4

おそらく、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.

2

open()およびのようなファイルシステムトラバーサルのためにOS固有の関数を呼び出す必要がありreaddir()ます。C標準では、ファイルシステム関連の関数は指定されていません。


C ++はどうですか?iostreamにそのような機能はありますか?
Aaron Maenpaa 2008

2
ファイルのみ。「ディレクトリ内のすべてのファイルを表示する」機能はありません。
1800情報

1
@ 1800:ディレクトリはファイルです。
軌道の軽さの競争

2

私たちは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_iteratorLegacyInputIterator、ディレクトリのdirectory_entry要素を反復し、再帰的にすべてのサブディレクトリのエントリを反復します。反復順序は指定されていませんが、各ディレクトリエントリは1回だけアクセスされます。

サブディレクトリのエントリを再帰的に反復したくない場合は、directory_iteratorを使用する必要があります。

どちらのイテレータもdirectory_entryのオブジェクトを返します。directory_entry以下のような種々の有用なメンバーの機能有しis_regular_fileis_directoryis_socketis_symlinkなどのpath()メンバ関数が返すのオブジェクトのstd ::ファイルシステム::パス、取得するために使用することができfile extensionfilename、を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;
}

1

あなたはしません。標準C ++は、ディレクトリの概念を公開していません。具体的には、ディレクトリ内のすべてのファイルを一覧表示する方法はありません。

恐ろしいハックは、system()呼び出しを使用して結果を解析することです。最も合理的な解決策は、QtPOSIXのようなクロスプラットフォームライブラリを使用することです。


1

使用できます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;
}

1
最後になりましたが、実際には以前の回答よりも優れています。
Seyed Mehran Siadati

0

Windowsを使用している場合は、FindFirstFileをFindNextFile APIと一緒に使用できます。FindFileData.dwFileAttributesを使用して、指定されたパスがファイルまたはディレクトリかどうかを確認できます。ディレクトリの場合は、アルゴリズムを再帰的に繰り返すことができます。

ここでは、Windowsマシン上のすべてのファイルをリストするいくつかのコードをまとめました。

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


0

ファイルツリーウォーク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;
 }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.