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

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

概要

 FindFirstFile APIとFindNextFile APIを用いてフォルダー内のファイル名をサブフォルダーの中も含めて検索するプログラムである。
 コマンドライン向けに相対パス・カレントフォルダー等に対応しました。
 j:¥フォルダーについてdirコマンドによる表示結果と本プログラムの表示結果を以下に示す。

本プログラムの実行結果 dir j:¥ /b /s
J:¥BROTHER>e:findfile3 j:
J:¥BROTHER      <DIR>
J:¥BROTHER¥mtouch2      <DIR>
J:¥BROTHER¥mtouch2¥Release      <DIR>
J:¥BROTHER¥mtouch2¥Release¥mtouch2.tlog <DIR>
J:¥BROTHER¥mtouch2¥Release¥mtouch2.tlog¥mtouch2.lastbuildstate
J:¥BROTHER¥mtouch2¥Release¥mtouch2.obj
J:¥BROTHER¥mtouch2¥mtouch2.cpp
J:¥BROTHER¥mtouch2¥mtouch2.vcxproj
J:¥BROTHER¥Release      <DIR>
J:¥BROTHER¥Release¥mtouch2.exe
J:¥BROTHER¥mtouch2.sln
J:¥BROTHER¥mtouch2.v12.suo

J:¥BROTHER>e:findfile3 ¥
J:¥System Volume Information    <DIR>
J:¥System Volume Information¥WPSettings.dat
J:¥System Volume Information¥IndexerVolumeGuid
J:¥BROTHER      <DIR>
J:¥BROTHER¥mtouch2      <DIR>
J:¥BROTHER¥mtouch2¥Release      <DIR>
J:¥BROTHER¥mtouch2¥Release¥mtouch2.tlog <DIR>
J:¥BROTHER¥mtouch2¥Release¥mtouch2.tlog¥mtouch2.lastbuildstate
J:¥BROTHER¥mtouch2¥Release¥mtouch2.obj
J:¥BROTHER¥mtouch2¥mtouch2.cpp
J:¥BROTHER¥mtouch2¥mtouch2.vcxproj
J:¥BROTHER¥Release      <DIR>
J:¥BROTHER¥Release¥mtouch2.exe
J:¥BROTHER¥mtouch2.sln
J:¥BROTHER¥mtouch2.v12.suo
J:¥mtouch2      <DIR>
J:¥mtouch2¥Release      <DIR>
J:¥mtouch2¥Release¥mtouch2.tlog <DIR>
J:¥mtouch2¥Release¥mtouch2.tlog¥mtouch2.lastbuildstate
J:¥mtouch2¥Release¥mtouch2.obj
J:¥mtouch2¥mtouch2.cpp
J:¥mtouch2¥mtouch2.vcxproj
J:¥Release      <DIR>
J:¥Release¥mtouch2.exe
J:¥mtouch2.sln
J:¥mtouch2.v12.suo
J:¥BROTHER
J:¥mtouch2
J:¥Release
J:¥mtouch2.sln
J:¥list.txt
J:¥System Volume Information¥WPSettings.dat
J:¥System Volume Information¥IndexerVolumeGuid
J:¥BROTHER¥mtouch2
J:¥BROTHER¥Release
J:¥BROTHER¥mtouch2.sln
J:¥BROTHER¥mtouch2¥mtouch2.cpp
J:¥BROTHER¥mtouch2¥mtouch2.vcxproj
J:¥BROTHER¥mtouch2¥Release
J:¥BROTHER¥mtouch2¥Release¥mtouch2.obj
J:¥BROTHER¥mtouch2¥Release¥mtouch2.tlog
J:¥BROTHER¥mtouch2¥Release¥mtouch2.tlog¥mtouch2.lastbuildstate
J:¥BROTHER¥Release¥mtouch2.exe
J:¥mtouch2¥mtouch2.cpp
J:¥mtouch2¥mtouch2.vcxproj
J:¥mtouch2¥Release
J:¥mtouch2¥Release¥mtouch2.obj
J:¥mtouch2¥Release¥mtouch2.tlog
J:¥mtouch2¥Release¥mtouch2.tlog¥mtouch2.lastbuildstate
J:¥Release¥mtouch2.exe
 コマンド引数にドライブ名を指定するとそのドライブのカレントフォルダを含むフォルダー・ファイルを表示します。
 コマンド引数に¥を指定するとカレントドライブに含まれるフォルダー・ファイルを表示します。

テスト環境

コンパイラ

Visual C++ 2013 Express 32/64bit

プロジェクトの作成

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

プログラムソースの概要

_tmain関数

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

FindFile

 検索されたフォルダー名・ファイル名を1個ごとにコールバック関数を呼び出します。
 検索する名称のフルパス名を取得します。(GetFullPathName)
 フルパス名の文字数が3より大きい場合、末尾に¥がある場合は削除して再度フルパス名を取得します。
 ファイル名部分がある場合(FilePart!=NULL)、ファイル名かフォルダー名をチェックしフォルダー名の場合、¥を末尾に付加し、まず今のフォルダー名を検索結果としてコールバック関数を呼び出します。
 ファイル名の場合は検索結果としてコールバック関数を呼び出し関数を終了します。
 最後にFindFileSubを呼び出して指定フォルダー内のフォルダー・ファイルを検索します。

FindFileSub

 フォルダ名の検索のワイルカードは*.*、ファイル名の検索には引数で指定されたワイルカードを用いるため、最初にフォルダー名を検索し、次にファイル名の検索を行います。
 フォルダー名が見つかった場合は、FindFileSubを再帰呼び出しを行います。
 フォルダー名・ファイル名のフルパス名は階層が深くなると長くなることが予測できます。
 文字数の最大値は、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メンバーの要素の最大数を示します。

EnumFileFunc

 コールバック関数です。
 フォルダー・ファイル1個ごとに呼び出されます。
 第1引数のfull_passメンバーがファイル名、第2引数がWIN32_FIND_DATA*です。
 第2引数のdwFileAttributesメンバーによりフォルダーかファイル名の判断ができます。

プログラムソース

// フォルダー内のファイル名をサブフォルダーも含めて検索する
// 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;	//	フルパスを保存
	volatile int stop_f;	//	検索の中止:TRUE
	FileFindParam(int max){
		max_path = max;
		level=len = 0;
		full_pass = new TCHAR[max_path];
		stop_f = FALSE;
	}
	~FileFindParam(){
		delete []full_pass;
	}
};

typedef void (*EnumFile)(FileFindParam*, WIN32_FIND_DATA*);

//	フォルダー内のファイル名をサブフォルダーも含めて検索する
void FindFile(FileFindParam* file_find_param,EnumFile func,int flags);
void FindFileSub(FileFindParam* file_find_param, EnumFile func);
void EnumFileFunc(FileFindParam* p, WIN32_FIND_DATA* data);

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

	param.pass = argv[1];// _TEXT("c:¥¥test");
	param.card = _TEXT("*.*");
	FindFile(&param, EnumFileFunc,TRUE);
}

void FindFile(FileFindParam* p,EnumFile func,int flags){
	TCHAR FullPath[MAX_PATH];
	TCHAR* FilePart;
	DWORD sz;

	sz = GetFullPathName(p->pass, sizeof(FullPath) / sizeof(TCHAR), FullPath, &FilePart);
	if (3<sz){
		if (FullPath[sz - 1] == _T('¥¥')){
			FullPath[sz - 1] = _T('¥0');
			TCHAR srcPath[MAX_PATH];
			_tcscpy_s(srcPath, sizeof(srcPath) / sizeof(srcPath[0]), FullPath);
			sz = GetFullPathName(srcPath, sizeof(FullPath) / sizeof(TCHAR), FullPath, &FilePart);
		}
	}
	if (FilePart != NULL){	//	ファイルまたはフォルダー
		WIN32_FIND_DATA FindFileData;
		HANDLE hFind;
		hFind = FindFirstFile(FullPath, &FindFileData);
		FileFindParam param(MAX_PATH_EX);
		param.len = _tcslen(p->pass);
		_tcscpy_s(param.full_pass, param.max_path, p->pass);
		if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){//フォルダー
			FileFindParam param(MAX_PATH_EX);
			param.len = _tcslen(p->pass);
			_tcscpy_s(param.full_pass, param.max_path, FullPath);
			_tcscat_s(FullPath, sizeof(FullPath) / sizeof(FullPath[0]), _TEXT("¥¥"));
			if (flags == TRUE)
				func(&param, &FindFileData);
			p->pass=FullPath;
			FindClose(hFind);
		}else{	//	ファイル名
			func(&param, &FindFileData);
			FindClose(hFind);
			return;
		}
	}
	p->pass = FullPath;
	FindFileSub(p,func);
}

void FindFileSub(FileFindParam* p,EnumFile func){
	if (p->stop_f == TRUE)
		return;
	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);
					func(p, &FindFileData);
					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;
					FindFileSub(p,func);	//	サブフォルダーの検索
					-- 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);
				func(p, &FindFileData);
			}
		} while (FindNextFile(hFind, &FindFileData));
		FindClose(hFind);
	}
}

void EnumFileFunc(FileFindParam* p, WIN32_FIND_DATA* data){
	if (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
		_tprintf(_TEXT("%s¥t<DIR>¥n"), p->full_pass);
	}
	else{
		_putts(p->full_pass);
	}
}

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

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