从perl中的输入文件一次有效地读取N行

时间:2014-07-31 18:49:48

标签: perl

我的输入数据文件的结构使得以N行的块读取数据更合乎逻辑,而不是一次读取一行。当然,我可以使用像

这样简单明了的东西
my @lines=();
while(!eof($FH)) {
  for(my $i=0;$i<$N;$i++)
   $lines[$i]=<FH>;
   chomp();
  }
  # proceed with analysis of N-size block ##
}

因为输入文件非常大(GB),我想知道是否存在比for循环更有效的解决方案。例如,我找到了另一个使用map函数的解决方案online,但是当我尝试在我的脚本中实现它时,会导致错误("my" variable @lines masks earlier declaration in same statement):

while(( my @lines = map $_ = <>, 1 .. 4 )[0]) {
  print @lines;
  print "\n";
}

不可否认,我不理解此代码的while块中[0]的重要性,而另一个解决方案建议使用[-1]

考虑到操作的I / O密集程度,我想知道这个问题的计算效率最高的解决方案是什么(在Perl编程语言的范围内)。

2 个答案:

答案 0 :(得分:5)

为简单起见,我可能会建议从主循环读取并添加到缓冲区:

my @buffer;

while (<$FH>) {
    push @buffer, $_;

    if (@buffer == $N || eof) {
        print @buffer;
        @buffer = ();
    }
}

从算法上讲,我不希望任何特定方法比任何其他方法快得多。您可以尝试使用其他方法从文件句柄中读取,但最终,我不希望发现任何主要的速度改进。

答案 1 :(得分:3)

到目前为止,任何文件IO中最慢的瓶颈都是磁盘本身。 Perl以任意大块的形式读取文件并在其中搜索换行符,以便它可以一次将数据传递给您。这意味着任何一次读取多行的方案只需要一小部分时间来从磁盘读取下一个块。因此,像往常一样,最普遍的标准是代码的可读性。

一旦我开始编码,我就会明白为什么最明显的解决方案是map。不幸的是,它看起来像这样

use strict;
use warnings;

use Data::Dump;

use constant N => 4;

while (my @block = grep defined, map { scalar <DATA> } 1 .. N) {
  dd \@block;
}

__DATA__
1
2
3
4
5
6
7
8
9

<强>输出

["1\n", "2\n", "3\n", "4\n"]
["5\n", "6\n", "7\n", "8\n"]
["9\n"]

但它可以写得更干净。到目前为止,我喜欢这个最好的

use strict;
use warnings;

use Data::Dump;

use constant N => 4;

until (eof DATA) {
  my ($rec, @block);
  push @block, $rec while @block < N and $rec = <DATA>;
  dd \@block;
}

__DATA__
1
2
3
4
5
6
7
8
9

具有相同的输出。

我正在考虑像

这样的事情
while (do { ... }) {
   dd \@block;
}

但我还没有!