9

Perl のタブで区切られたファイルからいくつかの列を取得しています。ファイルの最初の行は他の行とはまったく異なるため、その行をできるだけ速く効率的にスキップしたいと考えています。

これは私がこれまでに持っているものです。

my $firstLine = 1;

while (<INFILE>){
    if($firstLine){
        $firstLine = 0;
    }
    else{
        my @columns = split (/\t+/);
        print OUTFILE "$columns[0]\t\t$columns[1]\t$columns[2]\t$columns[3]\t$columns[11]\t$columns[12]\t$columns[15]\t$columns[20]\t$columns[21]\n";
    }
}

おそらく $firstLine なしで、これを行うより良い方法はありますか? または、2 行目から直接 INFILE の読み取りを開始する方法はありますか?

前もって感謝します!

4

7 に答える 7

29

これについていくつかのデータを取得しましょう。みんなのテクニックをベンチマークしました...

#!/usr/bin/env perl

sub flag_in_loop {
    my $file = shift;

    open my $fh, $file;

    my $first = 1;
    while(<$fh>) {
        if( $first ) {
            $first = 0;
        }
        else {
            my $line = $_;
        }
    }

    return;
}

sub strip_before_loop {
    my $file = shift;

    open my $fh, $file;

    my $header = <$fh>;
    while(<$fh>) {
        my $line = $_;
    }

    return;
}

sub line_number_in_loop {
    my $file = shift;

    open my $fh, $file;

    while(<$fh>) {
        next if $. < 2;

        my $line = $_;
    }

    return;
}

sub inc_in_loop {
    my $file = shift;

    open my $fh, $file;

    my $first;
    while(<$fh>) {
        $first++ or next;

        my $line = $_;
    }

    return;
}

sub slurp_to_array {
    my $file = shift;

    open my $fh, $file;

    my @array = <$fh>;
    shift @array;

    return;
}


my $Test_File = "/usr/share/dict/words";
print `wc $Test_File`;

use Benchmark;

timethese shift || -10, {
    flag_in_loop        => sub { flag_in_loop($Test_File); },
    strip_before_loop   => sub { strip_before_loop($Test_File); },
    line_number_in_loop => sub { line_number_in_loop($Test_File); },
    inc_in_loop         => sub { inc_in_loop($Test_File); },
    slurp_to_array      => sub { slurp_to_array($Test_File); },
};

これは Benchmark.pm の調整能力を超えた力の影響を受ける可能性がある I/O であるため、それらを数回実行し、同じ結果が得られることを確認しました。

/usr/share/dict/wordsは、約 240k の非常に短い行を含む 2.4 MB のファイルです。行を処理していないため、行の長さは問題になりません。

テクニックの違いを強調するために、各ルーチンでほんの少しの作業しか行いませんでした。ファイルの読み取り方法を変更することで、パフォーマンスがどれだけ向上または低下するかについて、現実的な上限を作成するために、いくつかの作業を行いたいと考えました。

SSDを搭載したラップトップでこれを行いましたが、それでもラップトップです。I/O 速度が向上するにつれて、CPU 時間はより重要になります。高速 I/O を備えたマシンでは、テクニックがさらに重要になります。

各ルーチンが 1 秒間にファイルを読み取る回数は次のとおりです。

slurp_to_array:       4.5/s
line_number_in_loop: 13.0/s
inc_in_loop:         15.5/s
flag_in_loop:        15.8/s
strip_before_loop:   19.9/s

私はそれmy @array = <$fh>が非常に遅いことを知ってショックを受けました。すべての作業が perl インタープリター内で行われていることを考えると、これが最速だと思っていたでしょう。ただし、すべての行を保持するためにメモリを割り当てる唯一のものであり、おそらくパフォーマンスの遅れを説明しています。

使用$.は別の驚きです。おそらくそれは、魔法のグローバルにアクセスするためのコストか、数値比較を行うためのコストです。

そして、アルゴリズム分析で予測されたように、ヘッダー チェック コードをループの外側に配置するのが最も高速です。しかし、それほどではありません。次の 2 つの最速を使用している場合は、おそらく心配するほどではありません。

于 2013-01-18T20:33:58.333 に答える
21

初めてダミー変数を割り当てることができます:

#!/usr/bin/perl
use strict;
use warnings;

open my $fh, '<','a.txt' or die $!;

my $dummy=<$fh>;   #First line is read here
while(<$fh>){
        print ;
}
close($fh);
于 2013-01-18T06:12:40.093 に答える
8

私は常に$.これを達成するために (現在の行番号) を使用します:

#!/usr/bin/perl
use strict;
use warnings;

open my $fh, '<', 'myfile.txt' or die "$!\n";

while (<$fh>) {
    next if $. < 2; # Skip first line

    # Do stuff with subsequent lines
}
于 2013-01-18T09:13:22.907 に答える
2

ファイルハンドルでファイルを読み取ってから、配列またはwhileループを使用して行を反復処理できます。whileループの場合、@Guruが解決策を提供します。アレイの場合、次のようになります。

#!/usr/bin/perl
use strict;
use warnings;

open (my $fh, '<','a.txt')  or die "cant open the file: $! \n";
my @array = <$fh>;

my $dummy = shift (@array);   << this is where the headers are stored.

foreach (@array)
{
   print $_."\n";
}
close ($fh);
于 2013-01-18T06:46:17.933 に答える
0

あなたのコードはおそらくこの形式でよりエレガントになるでしょう:

my $first;
while (...) {
    $first++ or next; 

    # do whatever you want
};

しかし、それでも問題ありません。@Guruの答えはCPUサイクルの点で優れていますが、I / Oは通常、単一の場合よりも桁違いに多くを消費します。

于 2013-01-18T10:25:14.660 に答える