9.7 ランダムアクセス
9.7.1 任意の位置を読み込む
これまでのファイルの読み込みは、ファイルの先頭から最後まで順番に行われました。ファイル関数を使って読み込むと、ファイル位置は次の項目に自動的に進むため、ファイルの先頭から順に入出力することができました。ストリームの次の読み書き対象バイトの情報をファイルポインタと呼びます。
ファイルの先頭から順に読み取るファイル形式をシーケンシャルアクセスと呼びます。単なるテキストデータの場合は、多くが順に読み取られたり書き込まれたりするため、非常に効率的です。しかし、バイナリデータの場合はこの方法だけでは問題が発生します。
バイナリファイルの場合は、異なる目的のデータが連続していることが多いと考えられます。例えば、最初の16バイトは位置とサイズ、その次には4バイトでアプリケーションの終了状態を表す数値、その次には前回の起動日時という具合です。これらの情報は個別のファイルに分けるよりも、ひとつにまとめてしまうほうがスマートなので多くのプログラマはこのように設計することでしょう。
しかし、先頭から20バイト目にある数値だけを読み込みたいだけであれば、これまでのように先頭から順番に読み込むという方法は最適ではありません。そこで、バイナリファイルの場合は任意の場所にアクセスすることができれば非常に便利であると考えられます。このような、特定の場所へのファイルアクセスをランダムアクセスと呼びます。
ランダムアクセスを実現するには、ファイルポインタの位置を移動させる必要があります。指定した位置にファイルポインタを移動させるには fseek() 関数を使います。
int fseek( FILE *stream, long offset, int origin );
stream には有効な FILE 型変数へのポインタを指定します。offset には origin で指定した位置から移動するバイト数を指定します。origin には、stdio.h ヘッダ内で定義されている次の定数のいずれかを指定します。stream で指定したストリームのファイルポインタは origin から offset だけ移動した場所に変更されます。
定数 | 意味 |
---|---|
SEEK_CUR | 現在のファイルポインタの位置 |
SEEK_SET | ファイルの先頭 |
SEEK_END | ファイルの終端 |
戻り値は、成功すると 0 を返し、そうでなければ 0 以外の値が返します。
もし、現在のファイルポインタの位置を知りたいのであれば ftell() 関数を使って得ることができます。
long ftell( FILE *stream);
この関数は、指定されたストリームのファイルポインタを返します。
#include <stdio.h> int main() { char fileName[256]; int fileIndex , text; FILE *file; printf("読み込むファイル名を指定してください>"); scanf("%s" , fileName); printf("ファイルを読み出す開始位置をバイト単位で指定してください>"); scanf("%d" , &fileIndex); file = fopen(fileName , "rb"); if (file == NULL) { fprintf(stderr , "ファイル操作でエラーが発生\n"); return 0; } fseek(file , fileIndex , SEEK_SET); printf("ファイル位置 %d から読み出します--\n" , ftell(file)); while(1) { text = fgetc(file); if (feof(file)) break; printf("%c" , text); } printf("\n"); fclose(file); return 0; }
コード1を実行すると、まずオープンするファイル名の入力を求められます。次に、指定したファイルの読み込む開始バイトを指定します。例えば 100 を指定すれば、指定したファイルの100バイト目から表示します。このプログラムは読み込んだデータを文字として標準出力に出力します。
コード1は fseek() 関数で SEEK_SET すなわちファイルの先頭から、入力された値 fileIndex バイト進んだ位置にファイルポインタを移動させています。こうすることによって、ファイル操作を行うときに目的とする特定の値だけを取得することができます。このようなランダムアクセスは、バイナリデータを管理する時に非常に有効となるでしょう。