プログラム - うごりぜいめんと

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

アプリケーションの下にある特定のファイル名のファイルを検索したい(C++)

普段は淡々と新曲報告している奴が急にどうしたかと思う方も多いかと思いますが…

最近、何を思ったかデレ○テにインスパイアされて作曲の方を少しお休みしてプログラミングで音ゲーを作ろうとしているところなのです。

製作はおなじみVisualStudio(C++) + DxLibで行っています。

そんなところで、B○S等のようなフリーの音ゲーのように追加で曲のファイルをどんどん入れられるようにしたいなと思い、それに伴い譜面ファイルを検索するプログラムを作ろうとしたのですが、これがなかなか難しくて…
他のゲームでもよくつかわれる機能だと思うのですが、自分が探した限りピンポイントでそういう機能の作り方の説明をしているところが見つからなかったので初心者なりに方法を書いておこうかと思います。実はすでにあったという場合は見なかったことにしてください。

ファイルを検索する際は、windows.hをインクルードして使えるFindFirstFile,FindNextFile,FindCloseという関数を使います。
早速FindFirstFileの関数を調べてみると、引数にWIN32_FIND_DATA戻り値にHANDLEという見慣れない型のようなものが出てきます。
WIN32_FIND_DATAは構造体型で、そこにファイルのデータを与えることができます
HANDLEは検索ハンドルと呼ばれるもので、これが有効な間は検索を続けます。

プログラムの大体の流れは
①FindFirstFileで最初に検索に該当するファイルのデータをデータ構造体に入れる
②①で戻り値として取得した検索ハンドルでFindNextFileを呼び出し、他に該当するものがないか探す。見つかったらデータ構造体に代入。
③②を見つからなくなるまで繰り返し、終わったらFindCloseで検索ハンドルを閉じる
というような感じです。

今回はフォルダの下のファイルを探すので、まずアプリ下に存在するファイルとディレクトリをすべて検索してから、ファイルの場合該当するファイルか、フォルダの場合はその下に該当するファイルがないかを再帰で調べることにします。

とりあえず、アプリ下にあるファイルとフォルダをすべて表示するコードを載せます。


#include
#include

void FindFile(HANDLE hFind, WIN32_FIND_DATA FindFileData, char* direc) {
if (hFind == INVALID_HANDLE_VALUE) return; //関数が失敗したら終了

do {
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { //ファイル属性がディレクトリの場合
if (strcmp(FindFileData.cFileName, "..") && strcmp(FindFileData.cFileName, ".")) { //下の階層だけ見る
printf("%s (DIR)\n", FindFileData.cFileName);
char s[CHAR_MAX] = "", s2[CHAR_MAX] = "";
sprintf_s(s, CHAR_MAX, "%s%s\\", direc, FindFileData.cFileName); //現在のディレクトリ\次のディレクトリ
sprintf_s(s2, CHAR_MAX, "%s\\*.*", s); //現在のディレクトリ\次のディレクトリ\*.*
HANDLE hFind2 = FindFirstFile(s2, &FindFileData); //そのディレクトリについて検索
FindFile(hFind2, FindFileData, s); //再帰
FindClose(hFind2);
}
}
else {
printf("%s%s\n", direc, FindFileData.cFileName);
}
} while (FindNextFile(hFind, &FindFileData)); //ファイルが見つからなくなるまで検索

}

int main()
{
HANDLE hFind; //検索ハンドル
WIN32_FIND_DATA FindFileData; //ファイルデータ

hFind = FindFirstFile("*.*", &FindFileData); //全てのファイル・ディレクトリについて検索
FindFile(hFind, FindFileData, ""); //アプリケーション下について関数を実行
FindClose(hFind); //検索ハンドルを閉じる

system("pause"); //一時停止

return 0;
}


プログラムの解説を少しします。

FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
:dwFileAttributesはデータ構造体のファイル属性を表すメンバです。これがディレクトリであるかどうか調べています。

strcmp(FindFileData.cFileName, "..") && strcmp(FindFileData.cFileName, ".")
:cFileNameはファイル名を表すメンバです。最初に検索してみたらなぜか上の階層を表す相対パスが引っかかってPC上のすべてのフォルダとファイルが表示されるという事態が起きてしまいました…とりあえず応急処置として相対パス的なものはぬかすようにしました。

そのほかはコード中のコメントを参照してください。

実際にこのコードを実行してみます。

ファイル階層はこんな感じです
dir.png
dir2.png
ここの一つ一つのフォルダにhumen.txtが入っています。

実行結果です。
result.png



これをsongsフォルダ下から検索しhumen.txtだけ出力するようにしてみます。


#include
#include


void FindFile(HANDLE hFind,WIN32_FIND_DATA FindFileData, char* direc, char* search){
if (hFind == INVALID_HANDLE_VALUE) return; //関数が失敗したら終了

do {
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { //ファイル属性がディレクトリの場合
if (strcmp(FindFileData.cFileName, "..") && strcmp(FindFileData.cFileName, ".")) { //下の階層だけ見る
//printf("%s (DIR)\n", FindFileData.cFileName);
char s[CHAR_MAX] = "", s2[CHAR_MAX] = "";
sprintf_s(s, CHAR_MAX, "%s%s\\", direc, FindFileData.cFileName); //現在のディレクトリ\次のディレクトリ
sprintf_s(s2, CHAR_MAX, "%s\\*.*", s); //現在のディレクトリ\次のディレクトリ\*.*
HANDLE hFind2 = FindFirstFile(s2, &FindFileData); //そのディレクトリについて検索
FindFile(hFind2, FindFileData, s, search); //再帰
FindClose(hFind2);
}
}
else {
//printf("%s%s\n", direc, FindFileData.cFileName);
if (!strcmp(FindFileData.cFileName, search)) { //探したいファイル名と一致していたら
printf("%s%s\n", direc, FindFileData.cFileName);//ファイルパスを表示
}
}
} while (FindNextFile(hFind, &FindFileData)); //ファイルが見つからなくなるまで検索

}

int main()
{
HANDLE hFind; //検索ハンドル
WIN32_FIND_DATA FindFileData; //ファイルデータ

hFind = FindFirstFile("songs\\*.*", &FindFileData); //song下の全てのファイル・ディレクトリについて検索
FindFile(hFind, FindFileData, "songs\\", "humen.txt"); //songs下についてhumen.txtを探す
FindClose(hFind); //検索ハンドルを閉じる

system("pause"); //一時停止

return 0;
}



printf("%s%s\n", direc, FindFileData.cFileName);
の部分を配列やらvectorやらにファイルパスを書き込むように改変すれば、立派にゲームに応用できる関数として使えます。

実行結果です
result2.png


以上でだいぶガバガバでしたが解説を終わりにします。
ブログの仕様でソースコードが見づらいと思うのでファイルの方もこちらに用意しました。ご活用ください。
(ファイルを消してしまいました…すみません…)

今回解説にあたってこちらのブログ様を参考にさせていただきました。ありがとうございます。

ここ間違ってるとか、もっといい方法があるとか、上手くいかないとかあったらどんどんコメントおねがいします。。。


とりあえず、これで私の方もゲーム製作が進みそうでよかったです。
あ、その前に中間試験が…
スポンサーサイト
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。