4

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

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

枪压缩文件是否有以下内容?

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...
}
4

3 回答 3

4

使用核心模块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变得非常慢并且内存占用很大。

于 2018-12-06T00:50:05.210 回答
3

Tie::File对于大文件来说是个坏主意,因为它需要一次将整个文件存储在内存中。对于压缩文件来说,这也是一个不切实际的想法,如果不是不可能的话。相反,您将希望对数据的输入流进行操作。如果您要修改数据,则输出流到数据的新副本。Perl 对通过层的 gzip 压缩提供了很好的支持PerlIO::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;
于 2018-12-06T00:49:58.417 回答
1

你写道:“在我的测试用例文件中,我只对阅读第 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 解决方案更快,因为zcat,tailheadinside qx 将成为三个进程,而 perl 是第四个。并且所有四个都可以自己获得一个单独的 cpu 内核。您可能想用不同的行号测试速度。

于 2018-12-08T01:40:31.267 回答