跳过文件的前x行和后y行

And*_*rew 3 perl

我正在对文本文件进行一些简单的解析(可能会达到1GB的范围)。我将如何跳过前N行,更重要的是,跳过最后(不同的)N行?我确定我可以打开文件并计算行数,并使用$ _ <total_row_count -N来执行某些操作,但这似乎效率很低。我差不多是个perl newb,顺便说一句。

zdi*_*dim 7

文件是一个字节序列,没有“行”的概念。这些字节中的某些字节被视为“行”分隔符(换行符),这是软件如何为我们提供“逻辑”行的方式。因此,没有办法知道一个文件中有多少行,而无需阅读并计数它们。

一种简单而天真的方法是逐行读取并计数

open my $fh, '<', $file  or die "Can't open $file: $!";

my $cnt;
++$cnt while <$fh>;
Run Code Online (Sandbox Code Playgroud)

使用变量更快的版本$.

1 while <$fh>;
my $cnt = $.;
Run Code Online (Sandbox Code Playgroud)

在合理的台式机上,一个1.1 Gb文本文件的时间在2.5到3秒钟之间。

我们可以通过读取较大的块并计算换行符来加快处理速度

open my $fh, '<', $file  or die "Can't open $file: $!";

my $cnt; 
NUM_LINES: {
    my $len = 64_000; 
    my $buf;

    $cnt += $buf =~ tr/\n// 
        while read $fh, $buf, $len;

    seek $fh, 0, 0;
};
Run Code Online (Sandbox Code Playgroud)

在相同的硬件和Perl版本上,仅用了不到半秒钟的时间。

我已经将它放在一个块中以限制不需要的变量,但是它应该在一个子变量中,然后您可以在其中检查文件句柄的位置,然后在计数后将其返回(因此我们可以对行的“剩余”数进行计数)从文件中的某个位置开始,然后可以继续进行处理等)。还应read在每次调用时包括对操作的检查。

我认为Gb大文件的半秒开销一点也不差。

不过,您可以更快,但要付出更多代价。获取文件大小(元数据,因此不涉及读取),并seek获取一个位置,该位置估计为结束前所需的行数(不涉及读取)。那很可能不会击中正确的位置,因此请读到最后以对行进行计数并进行调整,然后向后搜索(进一步或接近)。重复直到到达所需位置。

open my $fh, "<", $file; 
my $size = -s $file;

my $estimated_line_len = 80;
my $num_last_lines     = 100;

my $pos = $size - $num_last_lines*$estimated_line_len;

seek $fh, $pos, 0; 

my $cnt;    
++$cnt while <$fh>; 

say "There are $cnt lines from position $pos to the end"; 

# likely need to seek back further/closer ...
Run Code Online (Sandbox Code Playgroud)

我想这应该可以在100毫秒内到达您的位置。注意这$pos很可能在一行内。

然后,一旦知道行数(或结束前所需行数的位置)seek $fh, 0, 0,便进行处理。或确实在子程序中包含此子程序,如前所述,该子程序将文件句柄放回返回之前的位置。