ファイルハンドルのポインタ移動する
seek
は、FILEHANDLE のファイルポインタをバイト位置でセットします。
POSITION はバイト数による位置を表しますが、バイトをどこから数えるのかを WHENCE で指定します。
0
ならファイルの先頭から数えます。1
なら現在のファイルポインタの位置から数えます。
2
ならファイルの最後から数えます。しかし、逆方向に数えなければならないため、POSITION には負の値を指定します。
次の例は、UTF-8 で "あいうえおかきくけこ" と書かれたテキストファイルをオープンし、
seek
を使ってファイルポインタを先頭から 6 バイト移動します。
そして、read
を使って 3 文字分だけ読み取っています。
open my $fh, '<:utf8', './sample.txt'; # "あいうえおかきくけこ"
seek $fh, 6, 0; # ファイルポインタを先頭から 6 バイト移動する
read $fh, my $str, 3; # ファイルポインタの位置から 3 文字を読み取る
utf8::encode($str); # Perl 内部文字列を UTF-8 外部文字列に変換
print $str, "\n"; # "うえお"
このコードは "うえお" と出力します。この結果を不思議に思うかもしれません。
utf8 モードでオープンされたファイルハンドルから read
を使ってデータを読み取る際に、取り出すデータ長は文字数で指定します。
そのため、seek
の引数の単位も文字数と思われるかもしれません。
しかし、実際には seek
の引数の単位はバイトですので注意が必要です。
残念ながら、文字数を単位にしてファイルポインタを移動する手段は用意されていません。
前述のサンプルコードは seek
を使って 6 バイトだけファイルポインタを移動しました。
ファイルの文字エンコーディングは UTF-8 のため、たまたま「ひらがな」なら 2 文字分だったというわけです。
ちょうど文字の区切りの位置にポインタが移動したため、read
で都合よく文字を読み取れたのです。
もしファイルポインタが文字の途中に移動すると、そこから取り出される文字は文字化けするはずです。
Fcntl モジュールを使うと、
WHENCE に定数 SEEK_SET (0
), SEEK_CUR
(1
),
SEEK_END (2
) を使うことができます。
use Fcntl 'SEEK_SET';
seek $fh, 6, SEEK_SET;
seek
は成功すれば 1 を返し、失敗すれば 0 を返します。
sysseek
は移動後のファイルポインタの位置を返しますが、
seek
はそうではありませんので注意してください。
もし、ファイルハンドルを sysread
や
syswrite
で操作する場合は、
seek
は使わないでください。
seek
も sysseek
もファイルポインタをバイト単位で移動する点では違いはありませんが、
PerlIO レイヤーを通すか通さないか、という違いがあります。
この違いによって予期せぬ結果を招く恐れがあります。
ファイルハンドルを sysread
や
syswrite
で操作する場合は
sysseek
を使ってください。