Perl 只读取特定的 gz 文件行

Perl reading only specific gz file lines

我正在尝试制作一个解析脚本来解析一个巨大的文本文件(超过 200 万行),该文件是 gunzip 压缩的。我只想解析文本文件中的一系列行。到目前为止,我已经使用 zgrep -n 找到提到字符串的两行,我知道这些字符串将开始和结束我感兴趣的文件部分。

在我的测试用例文件中,我只对读取第 123080 行到 139361 行感兴趣。我发现 Tie::File 可以使用数组对象 it returns 访问文件行,但不幸的是不适用于我正在使用的枪压缩文件。

是否有类似下面的压缩文件?

use Tie::File
tie @fileLinesArray, 'Tie::File', "hugeFile.txt.gz"
my $startLine = 123080;

my $endLine = 139361;    
my $lineCount = $startLine;
while ($lineCount <= $endLine){
    my $line = @fileLinesArray[$lineCount]
    blah blah...
}

Tie::File 对于大文件来说不是个好主意,因为它需要一次将整个文件存储在内存中。对于压缩文件,这也是一个不切实际的想法,如果不是不可能的话。相反,您将希望对数据的输入流进行操作。如果你要修改数据,输出流到数据的新副本。 Perl 通过 PerlIO::gzip 层对 gzip 压缩提供了很好的支持,但您也可以通过一个或两个 gzip 进程传输数据。

# I/O stream initialization
use PerlIO::gzip;
open my $input, "<:gzip", "data.gz";
open my $output. ">:gzip", "data.new.gz";    # if $output is needed

# I/O stream initialization without PerlIO::gzip
open my $input, "gzip -d data.gz |";
open my $output, "| gzip -c > data.new.gz";

设置输入(和可选输出)流后,您可以像使用任何其他文件句柄一样对它们使用 Perl 的 I/O 工具。

# copy first $startLine lines unedited
while (<$input>) {
    print $output $_;
    last if $. >= $startLine;
}

while (my $line = <$input>) {
    # blah blah blah
    # manipulate $line
    print $output $line;
    last if $. >= $endLine;
}

print $output <$input>; # write remaining input to output stream
close $input;
close $output;

使用核心模块IO::Uncompress::Gunzip:

use IO::Uncompress::Gunzip;

my $z = IO::Uncompress::Gunzip->new('file.gz');
$z->getline for 1 .. $start_line - 1;
for ($start_line .. $end_line) {
    my $line = $z->getline;
    ...
}

Tie::File 处理大文件时变得非常慢并且占用内存。

您写道:“在我的测试用例文件中,我只对读取第 123080 到 139361 行感兴趣”。

这也可以在 shell 中完成:

zcat file | tail -n +123080 | head -16282

或通过:

my $file = 'the_file.gz';
my($from,$to) = (123080,139361);
my @lines = qx( zcat $file | tail -n +$from | head -@{[-$from+$to+1]});

这可能比普通的单核纯 perl 解决方案更快,因为 qx 中的 zcattailhead 将成为三个进程,而 perl 是第四个。所有四个都可能获得一个单独的 cpu 核心。您可能想用不同的行号测试速度。