フォルダー内のファイル名をサブフォルダーも含めて検索する

icon 項目のみ表示/展開表示の切り替え

概要

FindFirstFile APIとFindNextFile APIを用いてフォルダー内のファイル名をサブフォルダーの中も含めて検索するプログラムである。
E:¥testフォルダーについてdirコマンドによる表示結果と本プログラムの表示結果を以下に示す。

dir E:¥test /b /s 本プログラムの実行結果
E:¥test¥findfile1
E:¥test¥findfile1.sdf
E:¥test¥findfile1.sln
E:¥test¥Release
E:¥test¥findfile1¥Debug
E:¥test¥findfile1¥findfile1.cpp
E:¥test¥findfile1¥findfile1.vcxproj
E:¥test¥findfile1¥findfile1.vcxproj.filters
E:¥test¥findfile1¥Release
E:¥test¥findfile1¥x64
E:¥test¥findfile1¥Debug¥findfile1.obj
E:¥test¥findfile1¥Debug¥vc120.idb
E:¥test¥findfile1¥Debug¥vc120.pdb
E:¥test¥findfile1¥Release¥findfile1.obj
E:¥test¥findfile1¥Release¥vc120.pdb
E:¥test¥findfile1¥x64¥Debug
E:¥test¥findfile1¥x64¥Debug¥findfile1.obj
E:¥test¥findfile1¥x64¥Debug¥vc120.idb
E:¥test¥findfile1¥x64¥Debug¥vc120.pdb
E:¥test¥Release¥findfile1.exe
E:¥test¥Release¥findfile1.pdb
E:¥test¥findfile1       <DIR>
E:¥test¥findfile1¥Debug     <DIR>
E:¥test¥findfile1¥Debug¥findfile1.obj
E:¥test¥findfile1¥Debug¥vc120.idb
E:¥test¥findfile1¥Debug¥vc120.pdb
E:¥test¥findfile1¥Release   <DIR>
E:¥test¥findfile1¥Release¥findfile1.obj
E:¥test¥findfile1¥Release¥vc120.pdb
E:¥test¥findfile1¥x64       <DIR>
E:¥test¥findfile1¥x64¥Debug     <DIR>
E:¥test¥findfile1¥x64¥Debug¥findfile1.obj
E:¥test¥findfile1¥x64¥Debug¥vc120.idb
E:¥test¥findfile1¥x64¥Debug¥vc120.pdb
E:¥test¥findfile1¥findfile1.cpp
E:¥test¥findfile1¥findfile1.vcxproj
E:¥test¥findfile1¥findfile1.vcxproj.filters
E:¥test¥Release <DIR>
E:¥test¥Release¥findfile1.exe
E:¥test¥Release¥findfile1.pdb
E:¥test¥findfile1.sdf
E:¥test¥findfile1.sln

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

プロジェクトの作成

Win32コンソールアプリケーション

実行環境

Windows 8.1 Enterprise 64bit
Windows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit

プログラムソースの概要

_tmain関数

_tsetlocale関数によりUNICODE文字を標準出力に正しく表示させるためにロケールを設定します。
FindFile関数が検索するパス名とワイルカードをFileFindParam構造体のメンバーに設定してからFindFile関数を呼び出します。

FindFile

フォルダ名の検索のワイルカードは*.*、ファイル名の検索には引数で指定されたワイルカードを用いるため、最初にフォルダー名を検索し、次にファイル名の検索を行います。
フォルダー名が見つかった場合は、FindFileを再帰呼び出しを行います。
フォルダー名・ファイル名のフルパス名は階層が深くなると長くなることが予測できます。
文字数の最大値は、ANSI版のAPIはMAX_PATHマクロで定義される260文字、UNICODE版のAPIではパスの前に¥¥?¥を前置することにより最大32,000ワイド文字なので、検索時のパス名を保存するために最大文字数分のバッファを確保します。
フォルダー名およびファイル名の検索は基本的に以下の似通ったものです。
FindFirstFile APIで検索条件を指定し最初の1個目のフォルダー・ファイルの情報を取得します。
戻り値は検索ハンドルです。
戻り値がINVALID_HANDLE_VALUEの場合は、ファイルが見つからないことを示しています。
検索されたファイル名に関する情報はFindFileData構造体に格納されます。
FindFileData構造体メンバーは以下の通りです。
DWORD  dwFileAttributes;       // 属性
FILETIME  ftCreateTime;        // 作成日時
FILETIME  ftLastAccessTime;    // 最終アクセス日時
FILETIME  ftLastWriteTime;     // 最終更新日時
DWORD  nFileSizeHigh;          // ファイルサイズ(上位32ビット)
DWORD  nFileSizeLow;           // ファイルサイズ(下位32ビット)
TCHAR  cFileName[MAX_PATH];    // ファイル名
TCHAR  cAlternateFileName[14]; // 8.3形式のファイル
メンバーdwFileAttributesはファイルの属性を示しておりFILE_ATTRIBUTE_DIRECTORY定数とのAND演算を実行し結果が0でなければフォルダー、0の場合はファイルを示します。
フォルダーを検索しているときは、ファイル属性をチェックしフォルダーの時、検索パスとフォルダー名を結合しフルパスを作成し、フォルダー名を表示後、末尾に¥を付加し、FindFileを呼び出しサブフォルダーを検索します。
ファイルを検索しているときは、ファイル属性をチェックしファイルのとき、ファイル名を表示します。 次のフォルダー名・ファイル名を検索するためには、FindNextFile APIを用います。戻り値が0の場合は、ファイルが見つからないことを示しているのでフォルダー名・ファイル名の検索を終了させます。
検索が終了したらFindCloseにより検索ハンドルを解放します。 FileFindParam構造体のfull_passメンバーが現在の検索されたフルパス名、lenメンバーがフルパスの文字数、levelメンバーが現在検索しているサブフォルダーの階層数、max_pathメンバーがfull_passメンバーの要素の最大数を示します。

プログラムソース

// フォルダー内のファイル名をサブフォルダーも含めて検索する
// Visual C++ 2013 32/64bit

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <locale.h>

#ifdef UNICODE
        #define MAX_PATH_EX 32000
#else
        #define MAX_PATH_EX MAX_PATH
#endif

struct FileFindParam{
        TCHAR* pass;    //      検索対象となるフォルダー名を指定
        TCHAR* card;    //      ワイルカード
        int max_path;   //      パス名の最大長
        int len;                //      full_passの長さを保存
        int level;              //      現在の階層数
        TCHAR* full_pass;       //      フルパスを保存

        FileFindParam(int max){
                max_path = max;
                level=len = 0;
                full_pass = new TCHAR[max_path];
        }
        ~FileFindParam(){
                delete []full_pass;
        }
};


//      フォルダー内のファイル名をサブフォルダーも含めて検索する
void FindFile(FileFindParam* file_find_param);

void _tmain(void){
        //      UNICODE文字を標準出力に正しく表示させるためにロケールを設定
        _tsetlocale(LC_ALL, _TEXT(""));
        FileFindParam param(MAX_PATH_EX);

        param.pass = _TEXT("E:¥¥test¥¥");
        param.card = _TEXT("*.*");
        FindFile(&param);
}

void FindFile(FileFindParam* p){
        if (p->len == 0){
                p->len = _tcslen(p->pass);
                _tcscpy_s(p->full_pass, p->max_path, p->pass);
        }
        int parent_len = p->len;
        WIN32_FIND_DATA FindFileData;
        HANDLE hFind;

        _tcscpy_s(p->full_pass + p->len, p->max_path - p->len, _TEXT("*.*"));
        hFind = FindFirstFile(p->full_pass, &FindFileData);      //      フォルダーの検索
        if (hFind != INVALID_HANDLE_VALUE){
                do{
                        if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
                                if (_tcscmp(FindFileData.cFileName, TEXT("..")) && _tcscmp(FindFileData.cFileName, TEXT("."))){     //      親ディレクトリは無視する
                                        _tcscpy_s(p->full_pass + p->len, p->max_path - p->len, FindFileData.cFileName);
                                        _tprintf(_TEXT("%s¥t<DIR>¥n"), p->full_pass);
                                        p->len = p->len + _tcslen(FindFileData.cFileName);
                                        p->full_pass[p->len] = _T('¥¥');
                                        p->full_pass[p->len + 1] = _T('¥0');
                                        ++p->len;
                                        ++ p->level;
                                        FindFile(p);    //      サブフォルダーの検索
                                        -- p->level;
                                        p->len = parent_len;
                                }
                        }
                } while (FindNextFile(hFind, &FindFileData));
                FindClose(hFind);
        }else
                return;

        _tcscpy_s(p->full_pass + p->len, p->max_path - p->len, p->card);
        hFind = FindFirstFile(p->full_pass, &FindFileData);      //      ファイル名の検索
        if (hFind != INVALID_HANDLE_VALUE){
                do{
                        if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){
                                _tcscpy_s(p->full_pass + p->len, p->max_path - p->len, FindFileData.cFileName);
                                _putts(p->full_pass);
                        }
                } while (FindNextFile(hFind, &FindFileData));
                FindClose(hFind);
        }
}

ソースファイルのダウンロード

ダウンロード findfile2.zip(1.12kByte)
ZIPファイルに含まれるファイル
findfile2.cpp