Как мне сказать в Perl, какой размер файла внутри архива gzip без распаковки всего файла? - PullRequest
6 голосов
/ 09 февраля 2011

У меня есть куча смехотворно больших файлов (размером в несколько гигабайт), которые имеют действительно высокую степень сжатия (1: 200 или лучше). Я должен обработать их и хотел бы показать какую-то оценку прогресса. По этой причине я хотел бы знать размер файла внутри .gz, чтобы я мог сравнить его с тем, что я уже вытащил.

Тем не менее, поскольку распаковка всего файла заранее каждый раз является довольно непомерной и бесполезной тратой времени, я бы хотел вычислить размер без этого.

Я знаю, что это возможно. Я могу просто открыть gzip-файлы с помощью Total Commander, и плагин Viewer покажет мне правильный размер. (Я знаю, что он не распаковывается, потому что он сразу показывает мне размер, что было бы невозможно при 10 ГБ файле внутри gzip.)

Возможно, есть несколько полей заголовка, которые содержат эту информацию.

Однако, просматривая документы различных модулей CPAN, я не смог найти ничего подходящего. IO :: Uncompress :: Gunzip позволяет мне получить заголовок , но он не содержит никакой информации о размере файла.

Есть предложения?

Ответы [ 2 ]

1 голос
/ 26 июля 2012

Как описано в комментариях выше, последние 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;
}

Для несжатых файлов размером более 4 ГБ вам нужно будет угадать, следует ли добавить 4 ГБ к полученному размеру на основе ожидаемого минимального коэффициента сжатия.

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 );
1 голос
/ 20 марта 2011

Просто для этого есть правильный ответ:

sub get_gz_size {
    my ( $gz_file ) = @_;
    my @raw = `gzip --list $gz_file`;
    my $size = ( split " ", $raw[1] )[1];
    return $size;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...