• ベストアンサー

巨大なテキストの最終行を取得するには

perl v5.10.0 built for i386-linux-thread-multi OS: Fedora 9 (Linux localhost.localdomain 2.6.25.11-97.fc9.i686 #1 SMP Mon Jul 21 01:31:09 EDT 2008 i686 athlon i386 GNU/Linux) 巨大なテキストファイルの最終行を効率良く取得する方法を探しています。 検索を駆使して 6 日 6 晩試行錯誤したのですが、遂に見付けられなかった為、此所で質問させて頂きます。 私が知っている方法は以下の 3 つですが、何れも環境に依存するか、或いは効率が悪い等の理由で不完全です。 ---------------- #!/usr/bin/perl my $filename = './47GiB.txt'; my $file; # 1. print `tail -n 1 $filename`; # 2. open $file, $filename; print +(<$file>)[- 1]; # print pop @{[<$file>]}; close $file; # 3. open $file, $filename; my $pos = 0; while (<$file>) { $pos = tell $file unless eof $file; } seek $file, $pos, 0; print <$file>; close $file; ---------------- tail コマンドの様に、瞬時に最終行を読み出す方法は無いのでしょうか ? 御回答宜しくお願いします。

  • Perl
  • 回答数5
  • ありがとう数4

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

すみません, 「1回は読まないといけない」はウソです. 一度ファイルの最後までシークして, あとは ・適当な分だけ戻る ・read かなにかで読み込む ・改行があったら, そこからあとを「最後の行」とする という感じでいけると思います. これだと実行時間はファイル全体の大きさに関係なく, 最後の行の長さにのみ依存するはずです. 最後の行に改行があるかないかまで考えるとちょっと嫌ですが.

_--_--_-_-
質問者

お礼

以下の様なコードを書いて見ました。 ---------------- use feature qw(:5.10); my $filename = './47GiB.txt'; my $final; open my $file, $filename or die "$filename: $!\n"; for (my $i = - 2;; $i --) { last if eof $file; seek $file, $i, 2; my $line = <$file>; next unless defined $line; chomp $line; last if $line eq ''; $final = $line; } say $final; close $file; ---------------- 最終行が空行の場合に $final は空文字列に成るべきかも知れませんが、一先ず実用上は問題無さそうです。 御回答有難う御座いました。

その他の回答 (4)

  • kabaokaba
  • ベストアンサー率51% (724/1416)
回答No.5

File::Readbacwards を使うってのは駄目なんだろうか. seekして最後から探してくれるCPANモジュール. PurePerlだし,必要な処理だけ移植するのも 難しくなさそう. PerlHacksで紹介されてるくらいだから きっと便利なものでしょう.

_--_--_-_-
質問者

お礼

File::ReadBackwards を使う事にします。 御回答有難う御座いました。 大変貴重なモジュールを御紹介頂き、重ねて感謝申し上げます。 http://search.cpan.org/dist/File-ReadBackwards/ReadBackwards.pm http://perldoc.jp/docs/modules/File-ReadBackwards-1.02/ReadBackwards.pod ---------------- use File::ReadBackwards; print File::ReadBackwards->new('./47GiB.txt')->readline;

回答No.4

ファイルの最後から指定行数を取得 http://www.din.or.jp/~ohzaki/perl.htm#File_Tail アルゴリズムとしてはANo.3と同じです。

_--_--_-_-
質問者

お礼

非常に参考に成りました。 御回答有難う御座います。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

「最終行」を得るためには, どうしてもファイルを 1回は読む必要があります. だったら素直に open(my $fh, $filename); my $lastline; $lastline = $_ while <$fh>; close $fh; print $lastline; でいいと思うんだけど, 気のせい?

_--_--_-_-
質問者

補足

御回答有難う御座います。 御提示頂いたコードを見て居て閃きました。 続きます。

  • pick52
  • ベストアンサー率35% (166/466)
回答No.1

こんなのありましたけど(参考URL参照)。 2. が単純な形で最も効率がいいように思いますけどなんかダメ だったんでしょうか。

参考URL:
http://tuka.s12.xrea.com/index.xcg?p=Perl#p5
_--_--_-_-
質問者

補足

御回答誠に有難う御座います。 質問文の 3. のコードは、其のサイトのコードから着想を得て書いた物です。然し、速度が遅いです。 現在手元に件のテキストファイルが有るのですが、最終行を取得するのに可也の時間が掛かって仕舞って居ます。 より速いアルゴリズムはないのでしょうか ?

関連するQ&A

  • CGI.pmで取得したファイルハンドルを変数にいれるとファイル名のみになる・・・

    perlにて、 use CGI; my $q = new CGI; my $ufh = $q->upload('file'); などで$ufhにファイルハンドルを受け取り、 この直後で、アップロードなどを行うと正常に行われるのですが、 別の関数に渡してからアップロードを行うとファイル名がついた0バイトのファイルがアップロードされます。 &test($ufh); test($){ $filename = $_[0]; while(read($filename , $buffer, 1024)){ $file .=$buffer; } my $basename; my $exp; my $files; my $newfile; my @files = split(/\\/, $file_name); ($basename, $exp) = split(/\./, $files[-1]); $newfile = $basename . "." . $exp; open(FILE, "> /tmp/$newfile"); binmode(FILE); print(FILE $files); close(FILE); } ファイルハンドルを変数に格納して持ちまわすことはできないのでしょうか? よろしくお願いします。

    • ベストアンサー
    • Perl
  • 行を指定して削除する方法PERL

    ある文字を検索して、その行を含む&1行前と2行後ろの行までを削除するスクリプトを書きたいのですが、上手くいきません。 検索して行番号を獲得して、 $rowという変数に入れました。 それをさらに $a :1行前 $b :2行後ろの行番号に格納しました。 問題は削除するところが上手く行きません。 next if で $aから$bの行番号を削除して、と頼んでいるのですが、空のファイルに上書きされてしまいます。 お願いです。この方法で何が間違っているかを教えてください! #!/opt/perl/5.8.0/bin/perl -w print "Content-type: text/html\n\n"; use CGI qw(:standard); use CGI::Carp qw/fatalsToBrowser/; $filename = "../XML/link.xml"; $new = "../XML/link.xsl"; open(FILE, $filename) or die "Can't open `$filename': $!"; while (<FILE>) { if($_ =~ /HRWeb/){ #print "$."; $row = $.; $a = $row-1; $b = $row+2; print "HRWeb delete rows $a through "; } } &delete ($a, $b); sub delete{ open( OLD, "< $filename" ); open( NEW, "> $new" ); while ( <OLD> ) { next if /$a/../$b/; # copy everything but $a through $b print NEW $_; }print "$a deleted $b"; close( OLD ); close( NEW ); rename( $filename, "$filename.orig" ); rename($new, $filename ); }

    • ベストアンサー
    • Perl
  • 指定の行数目から行を抽出する

    いつもお世話になっております. 環境はWindows XP Pro でActiveperlを使っています. Perlでしたいことは,「指定の行数目から行を抽出する」ことです. 具体的には以下のようにしたいと思っております. data.txt A B C D E F line.txt 2 4 6 output.txt B D F 先ほどある方からサンプルソースを教えてもらったのでそれをベースに作ってみましたが,出力のoutput.txtが空のままです. use strict; use warnings; use feature ':5.10'; use IO::File; open my $file2, '<', 'line.txt' or die "can't open input $!"; chomp(my @subjects = <$file2>); close $file2; open my $newfile, '>>', 'data_out.txt' or die "can't open output $!"; open my $file, '<', 'data.txt' or die "can't open input $!"; while (my $line = <$file>) { chomp $line; foreach my $line (@line) { print $line; if ($. eq $subjects){ say {$newfile} $line; } } } close $file; close $newfile; どこが間違っているのでしょうか.ご指摘ください.よろしくお願いします.

    • ベストアンサー
    • Perl
  • 改行コードだけの行が現れるまでを1行として取得

    PHPでインプットファイルから、 改行コードだけの行が現れるまでを1行として取得しようと思っています。 下記例のように、 fgetsで行を取得しようとしたら、 改行が現れた時点までを1行としてしまうので、 少し違った1行の取得となってしまいました。 どうすればPHPで実現できるのか教えて欲しいです。 【例】 this is a pen. good-bye. ↓ this is a pen.good-bye. を1行分として取得したい。 # PHP 5.3.9 # CentOS 5.8 ---- $inputpath = '/home/test/test.txt'; $file = fopen($inputpath,"r") or die("open error $inputpath"); flock($file,LOCK_SH); while (!feof($file)){ $string = fgets($file,10000); print "$string"; print "\n"; } //relese the lock flock($file, LOCK_UN); //close the file fclose($file); ----

    • ベストアンサー
    • PHP
  • 圧縮ファイルの取得

    http://okwave.jp/qa/q8823112.html こちらで質問させていただいたように、/public_html/file.zipではなく、/file.zipの ファイルをダウンロードさせるために以下の方法を取りました。 print "Content-type: application/x-tar\n\n"; open( FH, "$filename" ); binmode FH; binmode STDOUT; print <FH>; close( FH ); この方法では、ブラウザで表示させてダウンロードできるようになったのですが、 これをperlで、wgetか、何かを使って取得したいと考えています。 ところが、wgetで試したところ、zipファイルではなく、cgiページ自体のソースの取得になってしまいました。 zipファイルを取得するにはどのようにしたらよいのでしょうか。 perlの知識があまりないので、どうしても解決できません。 宜しければ具体的に書き方を教えていただけると大変助かります。どうぞよろしくお願いいたします。

    • ベストアンサー
    • Perl
  • はじめまして

    はじめまして 現在、PerlのMVCフレームワークCatalystのperlソースコード内で ボタンを押すと、ユーザーにとあるファイルをダウンロードさせたい と考え、以下のように実装しました my $filedir = '/var/www/html/Catal/lib/Catal/Controller/'; my $filename = 'test.zip'; my $filepath = $filedir . $filename; print "Content-type:application/download; name=\"$filename\"\n"; print "Content-Disposition: attachment; filename=\"hogehoge\"\n\n"; open(FILE,"< $filepath"); binmode FILE; binmode STDOUT; print while(<FILE>); close(FILE); 上記コードを動かすとIEのページにZIPファイルがバイナリ表示されるだけで ダウンロードダイアログすら出ません。 どうしたら、ダイアログを表示させ、ユーザーが望む場所にダウンロード させることができるでしょうか。 よろしくお願いします。

  • ファイルの3行目までを出力したい

    Perl初心者です。 test.txtというファイルがあって、その中の1行目から3行目までを 出力したい場合はどうしたらいいでしょうか? open(FILE,"test.txt") || die "Open Error.\n"; @data = <FILE>; close(FILE); foreach (@data) { print $_; } これだと、ファイルの中身が全て出力されてしまいます。

    • ベストアンサー
    • Perl
  • perlでCSV形式ファイルのアップロード

    本日、同じ質問をしたものです。 前回の問題は解決したのですが、ファイルがアップロードできないで悩んでします。 CGIのプログラムは以下のソースです。 #! c:/perl/bin/perl use CGI; # CGIヘッダーの出力 print "Content-type: text/html\n\n"; my ($query,$fileName); $query = new CGI; $fileName = $query->param('fileName'); open(OUT, ">./a.csv"); binmode(OUT); while(read($fileName,$buffer,1024)) { print OUT $buffer; } close(OUT); close($fileName); #ファイルハンドルをcloseしています。 exit ; ファイルは、作られるのですが、中身が書き込まれません。 どなたか教えてください。 お願いします。

    • ベストアンサー
    • Perl
  • テキストファイルの行抽出

    linux環境のプログラムについて質問です。 ある特定のファイル(テキストファイル)内のデータで 指定の行を抽出する方法を教えていただきたいと思います。 現在はawkを使用してbashスクリプト内で下記のように head, tailを使用していますが、処理が重いように 感じます。perlまたはawkなどで行抽出の軽い処理は できないでしょうか? (他のunixコマンドでも結構です。) ---------------------------------------- RNUM=`awk 'END{print NR}' $1` #行番号取得 for iwl in `seq 1 $RNUM`;do BASE=`head -n $iwl $1 | tail -n 1` done #iwl ---------------------------------------- Fortran, Cなども使えますが、色々組み合わせて使う上で bash内のスクリプトで行ないたいと思います。

  • CGIでファイルが読み込めない

    CGI初心者です。 (osはlinuxです。) #!/usr/local/bin/perl use strict; use CGI; my $file=new CGI; open(DATA,"/home/data.txt"); $file=<DATA>; close=(DATA); print "Content-type: text/html\n\n"; print "data=$file"; このスクリプトで、/home/data.txt(フルパスです)に書かれているデータを1行だけをブラウザで表示させたいのですが、表示できません。 どこがおかしいのか教えてください。 ちなみにdata.txtには実際に「abc」という文字1行しか入れていません。

    • 締切済み
    • CGI

専門家に質問してみよう