ファイルハンドルから固定長のバッファデータを取り出す
read
は、ファイルハンドル FILEHANDLE を、現在のファイルポインタの位置から LENGTH 分の文字数だけ読み取って、
そのデータをスカラー変数 SCALAR にセットします。
そして、read
は、実際に読み取った文字数を返します。
もし何かしらのエラーが発生したなら undef
が返されます。
その場合、そのエラー内容は $! にセットされます。
次の例は、UTF-8 で「あいうえおかきくけこ」とだけ書かれたテキストファイル sample.txt
をオープンし、
最初の 6 文字を取り出しています。
# ファイルデータを UTF-8 エンコーディングとみなして Perl 内部文字列にして読み取った場合
open my $fh, '<:utf8', './sample.txt'; # "あいうえおかきくけこ"
my $str = '';
read $fh, $str, 6; # 先頭から 6 文字を読み取る
close $fh;
utf8::encode($str); # Perl 内部文字列を UTF-8 外部文字列に変換
print $str, "\n"; # "あいうえおか"
open
関数を使ってテキストファイルを読み取る際に、
'<:utf8'
を指定している点に注目してください。
これは、ファイルデータを UTF-8 エンコーディングのテキストとみなして Perl 内部文字列にして読み取っています。
この場合、read
関数の引数である LENGTH の単位は文字数になります。
一方、次の例では、ファイルデータをエンコーディングを指定せずにそのまま読み取っています。
# ファイルデータをそのまま読み取った場合 (事実上、バイナリデータとして読み取った場合)
open my $fh, '<', './sample.txt'; # "あいうえおかきくけこ"
my $str = '';
read $fh, $str, 6; # 先頭から 6 バイトを読み取る
close $fh;
print $str, "\n"; # "あい"
この場合、read
関数の引数である LENGTH の単位はバイト数になります。
たまたま、ファイルの文字が UTF-8 の 3 バイト文字のみで構成されていたため、
区切りの良い位置で切り取られ、取り出されたデータは文字化けしませんでしたが、もし 3 の倍数でない場所で切り取ると、
文字として認識できず文字化けします。
read
に引数 OFFSET を指定すると、引数 SCALAR の
OFFSET の位置から読み取ったデータを上書きします。
OFFSET は、ファイルハンドルの読み取り開始位置ではないので注意してください。
もし OFFSET が 3 なら、SCALAR の先頭から 3 文字はそのままに、それ以降の 4 文字目から置換されます。 しかし、たとえ事前に SCALAR に長い文字列がセットされていたとしても、 差し替えた文字列以降は切り詰められます。
open my $fh, '<:utf8', './sample.txt'; # "あいうえおかきくけこ"
my $str = '0123456789';
read $fh, $str, 2, 3; # 先頭から 2 文字を読み取り、3 文字目の次から書き込む
close $fh;
utf8::encode($str); # Perl 内部文字列を UTF-8 外部文字列に変換
print $str, "\n"; # "012あい"
もし OFFSET が SCALAR の文字数より大きい場合、
SCALAR の空いた部分は \0
で埋められます。
open my $fh, '<:utf8', './sample.txt'; # "あいうえおかきくけこ"
my $str = '';
read $fh, $str, 2, 3; # 先頭から 2 文字を読み取り、4 文字目から書き込む
close $fh;
utf8::encode($str); # Perl 内部文字列を UTF-8 外部文字列に変換
print $str, "\n"; # "あい"
print unpack( 'H*', $str ), "\n"; # "000000e38182e38184"
上記サンプルのように、\0
で埋められたとしても、文字列として出力すると、それが見えません。
unpack
を使って 16 進数にすると、
\0
で埋められていることが良く分かります。
open
でファイルハンドルを生成したとき、
ファイルポインタはファイルの先頭を指しています。
read
を使ってデータを読み取ると、ファイルポインタは読み取ったデータの最後の位置に移動します。
もし続いて read
を使ってデータを読み取ると、先ほどの続きから読み取ることになります。
open my $fh, '<:utf8', './sample.txt'; # "あいうえおかきくけこ"
read $fh, my $str1, 3; # 先頭から 3 文字を読み取る
utf8::encode($str1); # Perl 内部文字列を UTF-8 外部文字列に変換
print $str1, "\n"; # "あいう"
read $fh, my $str2, 3; # 続きから 3 文字を読み取る
utf8::encode($str2); # Perl 内部文字列を UTF-8 外部文字列に変換
print $str2, "\n"; # "えおか"
close $fh;
read
と良く似た組み込み関数に sysread
がありますが、
その違いは PerlIO レイヤーを通すか通さないかです。
もし純粋にバイナリーデータとして読み取りたい場合は
sysread
を使うのが良いでしょう。