ファイルの最後の行を取得する

2023/6/25 [01:45:40] (日) 天気

perlを使ってテキストファイルの最後の行を取得したい、というネタ。

tailでええやんということだけど、外部コマンドを使わないでperlだけで完結させたい。


ファイルを全部読み込んで最後の行だけを取り出す。

open(FH, $file); my @buf=<FH>; close(FH); pop(@buf)

これが一番簡単だし、ファイルを配列に全部読み込むのでその後の展開も応用にも使える、ド定番なやりかた。

読み込む、処理したいのがテキストファイルなら、ファイルサイズもしれてるだろうから、これで十分。わたしもいつもは何をするにもこのやり方。


だけど、データベースに食わせる元ネタのcsvファイルとかログファイルだと、ファイルサイズがギガバイトサイズのものがあったりして頭が頭痛。

最終行だけを知りたいのに、そんな阿呆みたいにでかいファイルを全部読み込むのは流石にもったいなくて馬鹿馬鹿しい、という貧乏性、ていうかリアルでビンボなんだけど。


ぐーぐる先生に聞いてみたら、検索結果からそのものズバリは見つけられなかったけど、seek()を使うのが良いらしいということはわかったのでスクリプトを書いてみた。


my $file = ’input.csv’;
my $filesize = (stat($file))[7];
open(FH, $file) || die;
my $pos = ($filesize - 3);
while(--$pos){
    my $buf;
    seek(FH, $pos, 0);
    read(FH, $buf, 1);
    last if $buf =~ m![\r\n]!;
}
my $line;
seek(FH, $pos+1, 0);
read(FH, $line, ($filesize - $pos) );
close(FH);
print $line;

seekはファイルハンドルのファイルポインタを任意の位置に設定する、つまり、開いてるファイルのどこを見るのかを指定できる。

ファイルサイズ=ファイルの末尾の位置から1バイトずつ先頭に向かってずらしていって、その位置から1バイト読み込んで、それが改行コードだったら、そこが最後の行の始まりということになる。


初期値をファイルサイズ-3としたのはファイル末尾に改行コードがあったらそこで終了してしまうから。

改行コードのバイト数はLFやCRだけなら1バイト、WINDOWSで一般的なCRLFは2バイト。なので、3バイト戻して1バイトを読むことで、最後の改行は無視できる。


まだ実際に大きなファイルに対して使ってないし、seek()で1バイトずつポインタをずらして1バイトずつ読み込んで、というのが本当に効率的なのかはよくわかってない。少なくとも、ファイルを頭っから順に読んでるわけじゃないのでファイルの大きさは関係ない、はず。

ファイルサイズが大きくなるにつれ、処理速度もそれにつれて遅くなるようなことにはならない。


とりあえずこいつを暫定首位に認定しておこう。


perlは本当にいろんなことをいろんなやり方でできるのが気持ち良い。

今回、ぐーぐる先生に聞いてみたら、古いエントリばかりが目立った、てことはもうあまり使われていない、ポピュラーな言語じゃなくなった、のかも知れない。

ていうか、この世界、あっという間に古びる。perlもすでに古代語となって、perl話者が少なくなってきたということかもしれない。ラテン語やサンスクリット語みたいなポジションになったのかもなあ。


だけど、perlはええぞ、マジで。

image
<<2026/1>>
    123
45678910
11121314151617
18192021222324
25262728293031
検索:

【最近の20件】