ページ 11

ファイルの探索 (Windows/C++)

Posted: 2011年6月04日(土) 23:22
by a5ua
Win32APIの
  • FindFirstFile
  • FindNextFile
  • FindClose
を使ったファイル探索をC++で書いてみました。作成環境は、Visual Stuio 2008 Express Editionです。

以下のヘッダファイル【enumerate_files.hpp】で機能を提供しています。

コード:

#pragma once

#include <windows.h>
#include <string>

/**
 * ファイルを探索する
 * @author a5ua
 * @tparam Callback ファイルが見つかったときに呼ばれる関数
 * @param path 探索を開始するディレクトリのパス
 * @param callback callback(path, find_data);という形で呼ばれる。<br>
 *                 pathはファイルが存在するディレクトリのパス、find_dataは見つかったファイルの情報
 */
template <typename Callback>
void enumerate_files(LPCWSTR path, Callback callback)
{
	HANDLE find_handle;			// ファイル探索用ハンドル
	WIN32_FIND_DATAW find_data;	// 探索情報

	// 探索するためのワイルドカードを設定
	std::wstring file_name(path);
	file_name += L"*.*";

	// 探索開始
	find_handle = FindFirstFileW(file_name.c_str(), &find_data);

	// ファイルが見つからなかったら終了
	if (find_handle == INVALID_HANDLE_VALUE) {
		return;
	}

	// ファイルが見つからなくなるまで繰り返し
	do {
		// 見つかったファイルに対する処理
		callback(path, find_data);
	} while (FindNextFileW(find_handle, &find_data));

	// 探索終了
	FindClose(find_handle);
}
FindFirstFileWなど、APIはワイド文字バージョンに限定しています。

ファイルが見つかったときに、どのような処理をするかは、全てcallbackに投げているので、
この関数の実装自体はシンプルになっています。
ただし、この関数は単体では動作しないので、適切にコールバック関数をつくってやる必要があります。

以下に、コールバックのサンプルとともに、enumerate_filesの使い方を示します。

コード:

#include <tchar.h>
#include <windows.h>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <iterator>
#include "enumerate_files.hpp"

/* コールバック用の関数オブジェクトのサンプル */

// ディレクトリを再帰的に探索して、ファイルパスのリストを作成する
class sample_callback
{
public:
	// ファイルリストの格納先
	sample_callback(std::vector<std::wstring> &files) : m_files(files)
	{
	}

	void operator()(LPCWSTR path, const WIN32_FIND_DATAW &find_data)
	{
		std::wstring name = find_data.cFileName;	// 見つけたファイル名

		// カレントディレクトリと親ディレクトリは無視
		if (name != L"." && name != L"..") {
			// 探索パスとファイル名から、見つけたファイルへのパスを作る
			std::wstring new_path(path);
			new_path += name;
			if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				// 見つけたファイルがディレクトリなら、再帰的に探索
				new_path += L"\\";
				enumerate_files(new_path.c_str(), sample_callback(m_files));
			} else {
				// ファイルパスを追加
				m_files.push_back(new_path);
			}
		}
	}
private:
	std::vector<std::wstring> &m_files;
};

/* enumerate_files の使い方サンプル */

int main()
{
	// 日本語が表示できるように
	std::wcout.imbue(std::locale("japanese"));

	const LPCWSTR path = L".\\";	// カレントディレクトリ以下を探索
	std::vector<std::wstring> v;	// ファイル名格納先

	// ファイルを列挙
	enumerate_files(path, sample_callback(v));

	// ファイル名表示
	std::copy(v.begin(), v.end(), std::ostream_iterator<std::wstring, wchar_t>(std::wcout, L"\n"));
}
コールバック関数の内容によって、
  • 再帰的に走査しない
  • *.cppファイルのみを列挙する
など、カスタマイズが可能になります。