2016年01月15日

Visual C++で2GB超のファイルを取り扱う方法

先日、仕事で作っていたWindows向けのツールで、3GBのファイルを扱う必要が出てきました。
C言語の標準のファイル入出力関数を使っていたプログラムだったのですが、どんだけ多くの改修を加えなければならないのかとビクビクしていたのですが、よくよく調べてみると、それほど難しくなかったみたいです。

というのも、ただ単にファイルを開いて、ファイルを入出力して、ファイルを閉じるくらいであれば、これまで通り fopen(), fgets(), fread(), fputs(), fwrite(), fclose() などの関数を使っておけばよいみたいで。

どんなときに注意が必要かというと、ユーザープログラム側とファイルポインタの現在位置情報のやりとりが生じるようなとき。
例えば、fseek() や ftell() を使う場合ですね。

fseek() は、ファイルポインタの現在位置を任意の位置に移動します。ところが、fseek()の引数はlong型です。Windowsのlongは、32bit CPUであっても64bit CPUであっても、32bit整数型ですので、ファイルポインタは 0〜231-1 の範囲でしか移動できません。
231-1 を超える位置にファイルポインタを移動させたければ、_fseeki64()を使うことになりなります。

ftell() は、ファイルポインタの現在位置を返す関数です。ところが、ftell()の戻り値はlong型です。
2GB超のファイルポインタの位置を取得したい場合は、_ftelli64()を使うことになります。

先のツールでは、ファイルサイズを得るために、


【2/17追記】【注意】下のような方法でファイルサイズを求めることを奨励しているサイトは多いのですが、fseek()でファイル終端位置にシークする操作は結果が保証されないことにされており、このようなコードを記述してはいけないそうです。fstat()を使うのが正しいやり方とのこと。

fseek(fp, 0, SEEK_END);
long fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);


というようなことをやっておりましたので、この部分を


【2/17追記】【注意】下のような方法でファイルサイズを求めることを奨励しているサイトは多いのですが、fseek()でファイル終端位置にシークする操作は結果が保証されないことにされており、このようなコードを記述してはいけないそうです。
_fseeki64(fp, 0, SEEK_END);
__int64 fsize = _ftelli64(fp);
_fseeki64(fp, 0, SEEK_SET);

に書き換えました。
ついでに、fsizeを表示するために printf("%ld", fsize); としていた箇所は、printf("%I64d", fsize); に差し替えです。

ちなみに、2GB以下のファイルに対しても、_fseeki64() や _ftelli64() は使用可能ですので、他の処理系への移植予定がなく、将来的に2GB超のファイルを扱う可能性があるならば、最初から _fseeki64() や _ftelli64() を使うようにして置いた方が良さそうですね。


posted by ayacy at 00:00 | Comment(0) | TrackBack(0) | プログラミング