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

con*_*con 7 gzip perl6 raku

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

  1. 如何在Perl6中逐行读取gz文件,然而,这个方法,读取所有东西: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() ;
Run Code Online (Sandbox Code Playgroud)

Inline::Perl5在Perl6中使用这样的东西:

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();
Run Code Online (Sandbox Code Playgroud)

但我看不出如何翻译这个:(

  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内存中的所有内容的情况下逐行读取大文件?

rai*_*iph 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 ;
Run Code Online (Sandbox Code Playgroud)

我用一个小的gzip文本文件测试了这个.

我对gzip等了解不多,但基于:

  • 知道P6;

  • 阅读Compress::ZlibREADME和选择zwrap程序;

  • 查看模块的源代码,特别zwrap例程 的签名our sub zwrap ($thing, :$zlib, :$deflate, :$gzip);

  • 反复试验,主要是为了猜测我需要通过:gzip副词.


请评论我的代码是否适合您.我猜主要的是它是否足够快你的大文件.

尝试解决方案#5失败

随着解决方案#2的工作,我本来希望能够只写:

use Compress::Zlib ;
.print for "data.txt.gz".&zwrap(:gzip).lines ;
Run Code Online (Sandbox Code Playgroud)

但那失败了:

No such method 'eof' for invocant of type 'IO::Path'
Run Code Online (Sandbox Code Playgroud)

这可能是因为这个模块是在重组IO类之前编写的.

这让我成为了@MattOates的IO::Handle对象.lines 问题.我注意到没有回复,我在https://github.com/MattOates?tab=repositories上看不到相关的回购.


Håk*_*and 5

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

对于调用$gz.gzreadline($_):似乎gzreadline尝试通过修改其输入参数$_(被视为输出参数,但它不是真正的Perl 5引用变量[1])来返回从zip文件读取的行,但修改后的值是没有返回到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;
Run Code Online (Sandbox Code Playgroud)

然后Inline::Perl5在这个包装器模块上使用而不是Compress::Zlib.例如./p.p6:

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();
Run Code Online (Sandbox Code Playgroud)

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

  • 你可以`使用lib:from <Perl5>'.';`来改变Perl5使用的路径.然后`使用MyZlibWrapper:来自<Perl5>;`将起作用. (3认同)