Re: テキストファイルの特定行を取得する方法
テキストファイルの特定行を取得する方法 - つれづれなるままに
sed を使う方法
sed は、ストリームエディタの略。
ファイルを対象に、行単位で加筆/修正/削除するツール。
# i=1
# sed -n "${i}P" /etc/hostsとコマンドを打てば、指定行(例では /etc/hosts の 1 行目)が表示される。
ループさせて、その1行の内容ごとに条件分岐させるとき、このやり方を使ってる。
head と tail を組み合わせる方法
head コマンドは、ファイルを上から n 行目まで表示させる。
tail コマンドは、ファイルを下から n 行目まで表示させる。
head コマンドで上から n 行目まで表示して、パイプで繋げて下から 1 行目を表示すれば、特定の行のみが表示される理屈。
# i=1
# head -${i} /etc/hosts | tail -1サンプルとしては、こんな感じ?
懸念される点として、1行ずつ取り出すけど、全体でン万行とか展開する必要があるとき、どれくらいタイムロスが発生するかなんだよね。
GNU の head のソースをざっくり見た感じでは、各行を fgets とかしてるわけではなく、表示しない行は lseek ですっ飛ばしているので心配なさげ。
でも一応いんちきベンチマークしてみました。
結論から言うと、head/tail も sed もそこそこ早いけど、sed のほうがちょっと早いっぽいです。
#!/usr/bin/perl # http://d.hatena.ne.jp/KuroNeko666/20090520/1242810069 use strict; use warnings; for my $lines ( qw(100000 1000000 10000000) ) { my $filename = "${lines}.txt"; write_file($lines); print "-----------------------------\n"; print "$lines lines (head/tail)\n"; system("sh -c \"time head -$lines $filename | tail -1\""); print "$lines lines (sed -n)\n"; system("sh -c \"time sed -n ${lines}P\ $filename\""); } sub write_file { my ($lines) = @_; open(my $OUT, '>', "${lines}.txt"); for ( my $i=1; $i<=$lines; $i++ ) { print {$OUT} "$i\n"; } close($OUT); }
行番号が書かれたファイルを作り、それの末尾を head/tail と sed で読んでみて、time コマンドで実行時間を取ります。行数は10万、100万、1000万行でやってみました
実行環境ですが、FreeBSD(head/tail/sed はシステム付属) と cygwin(head/tail/sed はシステム付属でGNU由来)でやってみました。
FreeBSD の場合
tsucchi@over[111]% perl a.pl ----------------------------- 100000 lines (head/tail) 0.02 real 0.00 user 0.01 sys 100000 100000 lines (sed -n) 100000 0.02 real 0.01 user 0.00 sys ----------------------------- 1000000 lines (head/tail) 0.27 real 0.00 user 0.15 sys 1000000 1000000 lines (sed -n) 1000000 0.17 real 0.15 user 0.00 sys ----------------------------- 10000000 lines (head/tail) 3.26 real 0.01 user 1.73 sys 10000000 10000000 lines (sed -n) 10000000 1.89 real 0.01 user 1.79 sys
cygwin の場合
tsucchi@appears[101]% perl a.pl ----------------------------- 10000 lines (head/tail) 10000 real 0m0.259s user 0m0.092s sys 0m0.062s 10000 lines (sed -n) 10000 real 0m0.179s user 0m0.046s sys 0m0.031s ----------------------------- 100000 lines (head/tail) 100000 real 0m0.095s user 0m0.060s sys 0m0.092s 100000 lines (sed -n) 100000 real 0m0.061s user 0m0.061s sys 0m0.016s ----------------------------- 1000000 lines (head/tail) 1000000 real 0m0.398s user 0m0.170s sys 0m0.388s 1000000 lines (sed -n) 1000000 real 0m0.395s user 0m0.390s sys 0m0.030s ----------------------------- 10000000 lines (head/tail) 10000000 real 0m4.788s user 0m1.592s sys 0m4.342s 10000000 lines (sed -n) 10000000 real 0m3.836s user 0m3.718s sys 0m0.155s
10万行、100万行ではほとんど差は無い感じ。1000万行では sed の方が早いみたいです。自分は head/tail 派だったのだけど、今後は sed を使ってみようかなぁ。