最終更新 2003 10/07
CreateFile 関数
CloseHandle 関数
ReadFile 関数
WriteFile 関数
SetFilePointer 関数
対応しているバージョン
95, 98, Me, CE1.0以降, NT3.1以降, 2000, XP
使用するライブラリとヘッダファイル
kernel32.lib
winbase.h
|
C言語と同じように fopen や fclose を使っても良いのですが、Win32標準の
ファイルの読み書き方法を説明します。
読み込み
1.CreateFile() でファイルを開く
2.ReadFile() でデータを読む
3.CloseHandle() でファイルを閉じる
書き込み
1.CreateFile() でファイルを開く
2.WriteFile() でデータを書く
3.CloseHandle() でファイルを閉じる
基本的には fopen なんかと操作は同じです。
関数に使う引数がゴチャゴチャ増えただけって感じですね。
では、1.から順番に説明していきます。
CreateFile() 関数の使い方は以下の通りです。
//ファイルを開く
HANDLE hFile = CreateFile(
"開くファイル名",
GENERIC_READ
( 書き込みの場合は GENERIC_WRITE ),
0,
NULL,
OPEN_EXISTING か OPEN_ALWAYS
( 書き込みの場合は CREATE_NEW か CREATE_ALWAYS ),
FILE_ATTRIBUTE_NORMAL か
FILE_ATTRIBUTE_READONLY か
FILE_ATTRIBUTE_HIDDEN,
NULL
);
//開けたかどうかチェックする
if( hFile == INVALID_HANDLE_VALUE )
{
/*ファイルのオープンに失敗*/
}
|
引数の補足説明
【 オープン方法を表す引数 】
OPEN_EXISTING 読み込み用。指定したファイルが存在しない場合
オープンに失敗する。
OPEN_ALWAYS 読み込み用。指定したファイルが存在しない場合
新規にファイルを作成する。
CREATE_NEW 書き込み用。指定したファイルが存在しない場合
オープンに失敗する。
CREATE_ALWAYS 書き込み用。指定したファイルが存在しない場合
新規にファイルを作成する。
※他にも指定できる値はありますが、まず使わないので割愛します。
| でつなぐことで複数の値を指定することができます。
【 属性を表す引数 】
FILE_ATTRIBUTE_NORMAL ファイルに属性をつけない。
FILE_ATTRIBUTE_READONLY ファイルを読み込み専用としてオープンする。
FILE_ATTRIBUTE_HIDDEN ファイルを隠しファイルとしてオープンする。
※他にも指定できる値はありますが、まず使わないので割愛します。
| でつなぐことで複数の値を指定することができます。
//読み書き両用で開く
HANDLE hFile = CreateFile(
"sample.txt",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING | CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL
);
|
2.ReadFile() でデータを読む(読み込み時)
|
ReadFile() 関数の使い方は以下の通りです。
DWORD dwNumberOfBytesToRead;
if( !ReadFile(
hFile,
データを格納するアドレスのポインタ,
読み込むバイト数,
&dwNumberOfBytesToRead,
NULL )
){
/* 読み込み失敗 */
CloseHandle( hFile );
}
|
ReadFile() を呼ぶ前に、hFile に CreateFile() で値を格納しておきます。
データを格納するアドレスのポインタは一概には言えませんが、バイナリデータが
多いと思います。
以下のような感じで処理すれば、だいたいのデータの読み込みには対応できると
思います。
上のサンプルでは、ファイルに保存されているデータを、確保したメモリに
一度に読み込んでしまっていますが、その方が効率が良いです。
読み込もうとしているファイルのサイズを知りたい場合は、上のサンプルの
ように GetFileSize() 関数を使います。
サンプルのような使い方だと 268,435,454 バイトまでのサイズしか調べる
ことができません。
もっとも、メインメモリが256Mしかないマシンで268Mのデータを
一度にメモリに読み込んでしまったら大変なことになりますが・・・。
ファイルのデータサイズが大きいほどその分のメモリを食いますが、必要な
データをひとつづつ読みながら処理をすると、とんでもなく処理が
遅くなります。
たかだか1Kバイト程度のデータを読み込むのに数十秒待たされたりする
ことになるので、ファイルのデータは一度に読むようにしましょう。
逆にファイルデータが100Mもある場合はそんなわけにはいかないので
そういう場合はファイルを開けっぱなしにしておいて、データの読む位置を
調節しながら、必要な分だけ読んで処理して、また必要な分だけ読んで
処理して・・・
という、いわゆるストリームという方法で処理します。
ゲームだとせいぜい、セーブデータの読み込みとか、マップデータの
読み込みなど、その辺の小さめのデータのやりとりしかしないので
ゲームのデータを効率良く管理する為に、独自に開発したゲーム用の
データベースを搭載する・・・などというケースでもなければ、ストリームを
使うことはないと思います。
あと、ファイルの読み込みを処理する場合、ファイルの読み込み位置が
データの最後まで達したかどうか(EOF)を調べたい場合があります。
EOFを調べるには ReadFile() 関数の戻り値を調べます。
DWORD dwNumberOfBytesToRead;
if( !ReadFile(
hFile,
データを読み込むアドレスのポインタ,
読み込むバイト数,
&dwNumberOfBytesToRead,
NULL )
){
//読み込み失敗
CloseHandle( hFile );
}
else
{
//ReadFile の戻り値が真で dwNumberOfBytesToRead が 0 の
//場合は EOF に達した
if( dwNumberOfBytesToRead == 0 )
{
//ファイルの読み込み位置が終端に達した
CloseHandle( hFile );
}
}
|
自分は最初MSDNで ReadFile() の説明を読んで混同してしまったのですが
ReadFile() の一番後ろの引数に NULL を指定した場合 GetLastError() 関数で
EOF を調べる事はできません。
2.WriteFile() でデータを書く(書き込み時)
|
WriteFile() 関数の使い方は以下の通りです。
DWORD dwNumberOfBytesToWrite;
//データをファイルへ書き込む
if( !WriteFile(
hFile,
データを格納しているアドレスのポインタ,
書き込むバイト数,
&dwNumberOfBytesToWrite,
NULL )
){
//書き込み失敗
CloseHandle( hFile );
}
//書き込むバイト数分、データを書き込めなかった
if( dwNumberOfBytesToWrite < 書き込むバイト数 )
{
//同じく書き込み失敗
CloseHandle( hFile );
}
|
3.CloseHandle() でファイルを閉じる
|
ファイルの読み書きが終了したら、最後に CloseHandle() 関数でファイルを
クローズします。
CloseHandle( hFile );
1.ファイルの読み書き位置を変更する
ファイルの読み書き位置は SetFilePointer() 関数で変更します。
使い方は以下のとおりです。
if( SetFilePointer( hFile, 移動量, NULL, 開始位置 ) == 0xffffffff )
{
//読み書き位置の変更に失敗
}
|
移動量 には 開始位置 から何バイト読み書き位置をずらすかを指定します。
+の値を指定すると、ファイルの終わりの方へ向かって位置を移動して
−の値を指定すると、ファイルの先頭の方へ向かって位置を移動します。
開始位置には次の値が指定できます。
FILE_BEGIN ファイルの先頭を開始点にします。
FILE_CURRENT ファイルポインタの現在位置を開始点にします。
FILE_END ファイルの終端を開始点にします。
ファイルの先頭から終わりへ向かって100バイト位置をずらしたい時は
SetFilePointer( hFile, 100, NULL, FILE_BEGIN );
とすればいいわけです。
ReadFile() を実行してデータを読み込む前に、念のためにファイルの先頭を
読み書き位置にしておきたいという場合は
SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
とすればよいのですが、CreateFile() を実行したときに、自動的にファイルの
読み書き位置は先頭に移動するようなので、あえて上のような処理を書く必要は
ないと思います。
1.ファイルの読み書き位置を取得する
ファイルの読み書き位置を調べるには
DWORD dwPosition = SetFilePointer( hFile, 0, NULL, FILE_CURRENT );
if( dwPosition == 0xffffffff )
{
//読み書き位置の取得に失敗
}
|
とすれば取得できます。
SetFilePointer() は戻り値として、移動した読み書き位置を返します。
ですから、開始点に現在位置の FILE_CURRENT を指定して、移動量に 0
(現在位置から移動しない) を指定すれば、現在位置が分かるというわけです。
|