我如何在Perl中告诉gzip存档中文件的大小是什么,而不解压缩整个文件?

时间:2011-02-09 15:59:55

标签: perl gzip

我有一堆非常大的文件(大小为几千兆字节),它们具有非常高的压缩比(1:200或更高)。我必须处理这些,并希望至少显示某种进度估计。出于这个原因,我想知道.gz中文件的大小,所以我可以将它与我已经提取的内容进行比较。

然而,由于每次提前打开整个文件包装是相当令人望而却步的,而且浪费时间,我想在没有这样做的情况下确定尺寸。

我知道这是可能的。我可以用Total Commander打开gzip文件,查看器插件会显示正确的大小。 (我知道它不会解压缩,因为它会立即向我显示大小,这对于gzip中的10GB文件来说是不可能的。)

可能有一些标题字段包含该信息。

然而,通过各种CPAN模块的文档,我找不到适合该法案的任何内容。 IO::Uncompress::Gunzip允许我访问 标头,但它不包含任何文件大小信息。

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

对此有正确答案:

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

答案 1 :(得分: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 );