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); },
};

由于这是 I/O,它可能会受到 Benchmark.pm 无法调整的力量的影响,所以我运行了几次并检查我得到了相同的结果。

/usr/share/dict/words是一个 2.4 兆的文件,大约有 240k 非常短的行。由于我们不处理线条,因此线条长度无关紧要。

我在每个例程中只做了少量的工作来强调技术之间的差异。我想做一些工作,以便通过更改读取文件的方式来确定您将获得或失去多少性能的现实上限。

我在带有 SSD 的笔记本电脑上执行此操作,但它仍然是笔记本电脑。随着 I/O 速度的提高,CPU 时间变得更加重要。在具有快速 I/O 的机器上,技术更为重要。

这是每个例程每秒读取文件的次数。

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 解释器中进行,我会认为这将是最快的。然而,它是唯一一个分配内存来保存所有行的,这可能是性能滞后的原因。

使用$.是另一个惊喜。也许这就是访问魔法全局的成本,或者可能是进行数字比较。

而且,正如算法分析所预测的那样,将标头检查代码放在循环之外是最快的。但不是很多。如果您使用接下来的两个最快,可能还不足以担心。

于 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 通常比单个 if 消耗更多数量级。

于 2013-01-18T10:25:14.660 回答
0

我有一个类似的问题/问题。我的解决方案如下 - 对于解压缩或 gzip 压缩的文件:

print STDERR "\nReading input file...\n";
if ($file =~ /.gz$/) {
    open(IN, "gunzip -c $file | grep -v '##' |") or die " *** ERROR ***     Cannot open pipe to [ $file ]!\n";
    } else {
        open(IN, "cat $file | grep -v '##' |") or die " *** ERROR ***     Cannot open [ $file ]!\n";
}

我不知道基准测试,但它对我来说很好。

最好的,

桑德

于 2017-05-04T21:43:08.737 回答
0

对我来说,使用 splice 似乎是最简单、最干净的方法:

open FILE, "<$ARGV[0]";
my @file = <FILE>;
splice(@file, 0, 1);

完毕。现在你的 @file 数组不再有第一行了。

于 2018-04-17T19:30:28.180 回答