在Perl中,如何在不解压整个文件的情况下确定gzip归档文件中文件的大小?

6
我有一堆非常大的文件(大小为几个GB),但它们具有非常高的压缩比(1:200或更好)。 我必须处理这些文件,并希望至少显示某种进度估计。出于这个原因,我想知道.gz文件中的文件大小,以便可以将其与已提取出的内容进行比较。
但是,由于每次预先解压整个文件都相当禁止且浪费时间,因此我想在不这样做的情况下弄清楚文件大小。
我知道这是可能的。 我可以使用Total Commander打开gzip文件,查看器插件将向我显示正确的大小。 (我知道它没有解压缩,因为它立即显示给我大小,这对于gzip内部的10GB文件实际上不可能。)
可能存在一些包含该信息的标题字段。
但是,在查看各种CPAN模块的文档时,我找不到任何符合要求的内容。 IO :: Uncompress :: Gunzip让我获得一个标题,但它不包含任何文件大小信息。
有什么建议吗?

3
没问题,您说得对 - 这里有 ISIZE 字段,详情请见此链接:http://www.gzip.org/zlib/rfc-gzip.html#header-trailer。 - Richard H
嗯,我猜除非有一个Perl API可以做到这一点,否则我的唯一选择就是手动读取文件的最后四个字节了? - Mithaldu
7
快速而简单的解决方案是解析 gzip --list 命令的输出。 - Ether
非常好,谢谢Ether!:D - Mithaldu
2个回答

1

为此提供一个正式的答案:

sub get_gz_size {
    my ( $gz_file ) = @_;
    my @raw = `gzip --list $gz_file`;
    my $size = ( split " ", $raw[1] )[1];
    return $size;
}

这个可以工作,但是它调用了gzip命令,而不是深入gzip文件中提取最后4个字节。 - ChuckCottrill
调用gzip是更好的选择,因为它避免了代码重复。这里的目标是避免解压整个文件,而不是避免gzip。 - Mithaldu

1

如上面的评论所述,最后4个字节包含isize

这是我编写的一些代码,用于计算给定文件路径的未压缩字节数:

sub get_isize
{
   my ($file) = @_;

   my $isize_len = 4;

   # create a handle we can seek
   my $FH;
   unless( open( $FH, '<:raw', $file ) )
   {
      die "Failed to open $file: $!";
   }
   my $io;
   my $FD = fileno($FH);
   unless( $io = IO::Handle->new_from_fd( $FD, 'r' ) )
   {
      die "Failed to create new IO::Handle for $FD: $!";
   }

   # seek back from EOF
   unless( $io->IO::Seekable::seek( "-$isize_len", 2 ) ) 
   {
      die "Failed to seek $isize_len from EOF: $!"
   }

   # read from here into mod32_isize
   my $mod32_isize;
   unless( my $bytes_read = $io->read( $mod32_isize, $isize_len ) )
   {
      die "Failed to read $isize_len bytes; read $bytes_read bytes instead: $!";
   }

   # convert mod32 to decimal by unpacking value
   my $dec_isize = unpack( 'V', $mod32_isize );

   return $dec_isize;
}

对于大于4GB的未压缩文件,您需要根据预期的最小压缩因子猜测是否将4GB添加到检索到的isize中。

use constant MIN_COMPRESS_FACTOR => 200;
my $outer_bytes = ( -s $path );
my $inner_bytes = get_isize( $path );
$bytes += 4294967296 if( $inner_bytes < $outerbytes * MIN_COMPRESS_FACTOR );

如果您的未压缩文件大于4294967296 * 2,则您将不得不猜测要应用多少个4294967296的倍数(尽管我从未测试过),但是您需要准确地判断预期的压缩比才能使其正常工作:
my $estimated_multiplier = int( ($outerbytes * MIN_COMPRESS_FACTOR) / 4294967296 );
$bytes += ( 4294967296 * $estimated_multiplier ) if( $estimated_multiplier );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接