ファイルのシークと位置取得

こんにちは、めのんです!

昨日は1日おやすみをいただきましたが、今日からは今まで通り更新していきますね。
今回はファイルのシークと位置取得です。

ファイル位置表示子

PHPでいう「ファイルポインタ」はCでは「ファイル位置表示子」といいます。
Cで「ファイルポインタ」といえばFILE型へのポインタのことになりますので意味が異なります。
言い方の違いはあっても、Cのファイル位置表示子はPHPのファイルポインタと同じ意味だと考えて問題ありません。

fseek関数とftell関数

ファイル位置表示子を設定・取得するには、基本的には「fseek関数」「ftell関数」を使います。
この2つの関数はPHPにも同名のものがあるのでおなじみでしはないでしょうか?

これらの関数は「stdio.h」で次のように宣言されています。

int fseek(FILE *stream, long int offset, int whence);
long int ftell(FILE *stream); 

PHPの同名の関数と比べていただければわかりますが、そっくりな形をしていると思います。
fseek関数の第3引数もPHPと同じでSEEK_SET、SEEK_CUR、SEEK_ENDのどれかを指定します。
もちろん意味も同じです。
ただし、fseek関数の第3引数を省略することはできませんので注意してください。

fseek関数でファイル位置表示子を設定し、ftell関数でファイル位置表示子を取得することができます。

ファイルをオープンする際にr+などをモードと指定すると読み書き可能になりますが、それまで読み込んでいたストリームに対して今度は書き込もうとするときにはファイル位置表示子を再設定する必要があります。
それまで書き込んでいたストリームから読み込もうとするときも同じです。

rewind関数

「rewind関数」もPHPに同名の関数がありますね。
基本的な機能も同じだと考えていただいてかまいません。

rewind関数は「stdio.h」ヘッダで次のように宣言されています。

void trwind(FILE *stream);

1点補足しておかないといけないのは、ファイル位置表示子を先頭に戻すだけではなくて、ファイル終了表示子とエラー表示子をクリアすることです。
オープン時のモードがaやa+でも先頭に戻しますので注意が必要です。

ファイル終了表示子というのは、ファイルからの読み込みの際にファイルの終端に到達したときにストリームにセットされるフラグです。
エラー表示子は、ファイルを読み書きする際にエラーが発生した場合にストリームにセットされるフラグです。
「clearerr関数」を呼び出せばクリアできるのですが、rewind関数の呼び出してもクリアすることができます。
clearerr関数についてはまた別の機会に解説しますね。

fsetpos関数とfgetpos関数

さきほどfseek関数とftell関数について紹介しましたが、Cにはもう一組ファイル位置表示子を設定・取得する関数があります。
「fgetpos関数」と「fsetpos関数」がそれです。
これらの関数はPHPにはありませんので、ちょっと詳しめに解説することにします。

まずは宣言からです。
これらの関数は「stdio.h」ヘッダで次のように宣言されています。

int fsetpos(FILE *stream, const fpos_t *pos);
int fgetpos(FILE *stream, fpos_t *pos);

どちらの関数もstreamとposの2つの引数を取ります。
streamはいつものおなじみでfopen関数が返したFILE型のポインタです。
posはファイル位置表示子を設定・取得するための値の格納先になります。
fpos_t型というのは処理系定義の(配列型以外の)オブジェクト型になります。

fsetpos関数がファイル位置表示子するためのもので、fgetpos関数が取得するためのものになります。
これは名前からわかりますよね。

fseek関数とftell関数の組み合わせに比べて、ファイルの中を行ったり来たりする場合にはfsetpos関数とfgetpos関数の組み合わせの方が見通しがよくなります。

ただし、fseek関数はファイル位置表示子をどこにでもいきなり設定することができるんですけど、fsetpos関数はあらかじめfgetpos関数で取得した場所にしか設定できませんので用途に応じて使い分ける必要があります。

もうひとつfsetpos関数とfgetpos関数の組み合わせには重要なメリットがあります。

fseek関数とftell関数ではファイル位置を表すのにlong型を使っています。
long型が32ビットの処理系は普通にありますので(32ビット以下のほとんどの処理系と64ビットのWindowsがそうです)、これだと2GiBまでしか表現できないんですよね。
その点、fsetpos関数とfgetpos関数の組み合わせであればそのような問題はありません。


というわけで、今回の解説は以上となります。
次回もファイル関係の解説をしようと思います。
どうぞご期待ください!