Perl6:大的压缩文件逐行读取

时间:2019-02-21 18:21:35

标签: gzip perl6

我试图在Perl6中逐行读取gz文件,但是,我被阻止了:

  1. How to read gz file line by line in Perl6,但是,这种方法将所有内容读入:out会占用过多的RAM,无法使用,除非是很小的文件。

  2. 我不了解如何使用Perl6的Compress::Zlib逐行获取所有内容,尽管我在他们的github https://github.com/retupmoca/P6-Compress-Zlib/issues/17

    上打开了一个问题
  3. 我正在尝试Perl5的Compress::Zlib来翻译此代码,该代码在Perl5中非常有效:

use Compress::Zlib;
my $file = "data.txt.gz";
my $gz = gzopen($file, "rb") or die "Error reading $file: $gzerrno";

while ($gz->gzreadline($_) > 0) {
    # Process the line read in $_
}

die "Error reading $file: $gzerrno" if $gzerrno != Z_STREAM_END ;
$gz->gzclose() ;

在Perl6中使用Inline::Perl5进行如下操作:

use Compress::Zlib:from<Perl5>;
my $file = 'chrMT.1.vcf.gz';
my $gz = Compress::Zlib::new(gzopen($file, 'r');
while ($gz.gzreadline($_) > 0) {
  print $_;
}
$gz.gzclose();

但是我看不到如何翻译:(

  1. 我对Lib :: Archive示例https://github.com/frithnanth/perl6-Archive-Libarchive/blob/master/examples/readfile.p6感到困惑,在这里我看不到如何获得第3项之类的东西

  2. 应该有类似

for $file.IO.lines(gz) -> $line {或Perl6中的类似内容(如果存在),我找不到它。

如何在不将所有内容都读入Perl6的RAM的情况下逐行读取大文件?

2 个答案:

答案 0 :(得分:7)

更新现在已测试,发现了一个错误,现已修复。

解决方案2

use Compress::Zlib;

my $file   = "data.txt.gz" ;
my $handle = try open $file or die "Error reading $file: $!" ;
my $zwrap  = zwrap($handle, :gzip) ;

for $zwrap.lines {
    .print
}

CATCH { default { die "Error reading $file: $_" } }

$handle.close ;

我已经用一个小的gzip压缩文本文件对此进行了测试。

我对gzip等不是很了解,但是基于以下原因发现了这一点:

  • 知道P6;

  • 阅读Compress::Zlib's README并选择zwrap例程;

  • 查看模块的源代码,尤其是the zwrap routine our sub zwrap ($thing, :$zlib, :$deflate, :$gzip)的签名;

  • 反复试验,主要是为了猜测我需要传递:gzip副词。


请评论我的代码是否对您有用。我猜最主要的是它对您拥有的大文件是否足够快。

尝试解决方案5失败

在解决方案#2正常工作的情况下,我希望能够编写:

use Compress::Zlib ;
.print for "data.txt.gz".&zwrap(:gzip).lines ;

但是失败了:

No such method 'eof' for invocant of type 'IO::Path'

大概是因为此模块是在IO类的重组之前编写的。

那导致我进入@MattOates' IO::Handle like object with .lines ? issue。我注意到没有任何回应,也没有在https://github.com/MattOates?tab=repositories上看到相关的回购。

答案 1 :(得分:5)

我专注于您尝试过的Inline::Perl5解决方案。

对于$gz.gzreadline($_)的调用:gzreadline似乎试图返回从zip文件by modifying its input argument $_中读取的行(被视为输出参数,但这不是真正的Perl 5参考变量 [1] ),但修改后的值不会返回到Perl 6脚本中。

这是一个可能的解决方法: 在curent目录中创建包装器模块,例如 ./ MyZlibWrapper.pm

package MyZlibWrapper;
use strict;
use warnings;
use Compress::Zlib ();
use Exporter qw(import);

our @EXPORT = qw(gzopen);
our $VERSION = 0.01;

sub gzopen {
    my ( $fn, $mode ) = @_;
    my $gz = Compress::Zlib::gzopen( $fn, $mode );
    my $self = {gz => $gz}; 
    return bless $self, __PACKAGE__;
}

sub gzreadline {
    my ( $self ) = @_;
    my $line = "";
    my $res = $self->{gz}->gzreadline($line);
    return [$res, $line];
}

sub gzclose {
    my ( $self ) = @_;
    $self->{gz}->gzclose();
}    

1;

然后在此包装器模块上使用Inline::Perl5而不是Compress::Zlib。例如 ./ p.p​​6

use v6;
use lib:from<Perl5> '.';
use MyZlibWrapper:from<Perl5>;
my $file = 'data.txt.gz';
my $mode = 'rb';
my $gz = gzopen($file, $mode);
loop {
    my ($res, $line) = $gz.gzreadline();
    last if $res == 0;
    print $line;
}
$gz.gzclose();

[1]  在Perl 5中,您可以修改不是引用的输入参数,并且更改将反映在调用方中。这是通过修改特殊@_数组变量中的条目来完成的。例如:sub quote { $_[0] = "'$_[0]'" } $str = "Hello"; quote($str)将引用$str,即使未通过引用传递$str