2011-02-09 7 views
6

Mam kilka absurdalnie dużych plików (wiele gigabajtów), które mają naprawdę wysoki współczynnik kompresji (1: 200 lub lepszy). Muszę je przetworzyć i chciałbym chociaż trochę oszacować postęp. Z tego powodu chciałbym znać rozmiar pliku wewnątrz .gz, więc mogę go porównać z tym, co już wyciągnąłem.Jak powiedzieć w Perlu, jaki jest rozmiar pliku wewnątrz archiwum gzip bez rozpakowywania całego pliku?

Ponieważ jednak rozpakowywanie całego pliku z góry za każdym razem jest dość wygórowane i jest stratą czasu, chciałbym określić rozmiar bez robienia tego.

Wiem, że to możliwe. Mogę po prostu otwierać pliki gzip za pomocą Total Commander, a wtyczka przeglądarki pokaże mi odpowiedni rozmiar. (Wiem, że to nie jest rozpakowywanie, ponieważ pokazuje mi natychmiast rozmiar, co nie byłoby możliwe z plikiem o pojemności 10 GB w gzipie.)

Prawdopodobnie istnieje kilka pól nagłówka, które zawierają te informacje.

Jednak przeglądając dokumenty różnych modułów CPAN nie mogłem znaleźć niczego, co pasowałoby do rachunku. IO::Uncompress::Gunzip pozwala mi uzyskać w nagłówku , ale nie zawiera żadnych informacji o rozmiarze pliku.

Wszelkie sugestie?

+3

masz rację - nie ma pole ISIZE, zobacz tutaj szczegóły: http://www.gzip.org/zlib/rfc- gzip.html # header-trailer –

+0

Hmm, więc chyba nie ma na to API Perla, moim jedynym odwołaniem jest ręczne odczytanie ostatnich czterech bajtów pliku? – Mithaldu

+7

Szybkim i brudnym rozwiązaniem byłoby przeanalizowanie danych wyjściowych 'gzip -list'. – Ether

Odpowiedz

1

Tak więc nie jest to właściwa odpowiedź na to:

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

Działa to, ale wywołuje polecenie gzip, zamiast przekopywania się do pliku gzip, aby wyodrębnić 4 ostatnie bajty. – ChuckCottrill

+0

Wywołanie gzip jest lepszym rozwiązaniem, ponieważ pozwala uniknąć duplikowania kodu. Celem było uniknięcie rozpakowania całej rzeczy, aby uniknąć gzipu. – Mithaldu

1

Jak opisano w powyższych uwag, ostatnie 4 bajty zawierają isize

Oto niektóre kodu I napisał do obliczenia nieskompresowanych bajtów podana ścieżka pliku:

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; 
} 

Dla nieskompresowanych plików większych niż 4 GB, musisz zgadnąć, czy dodać 4 Gb do Odzyskujemy e, w oparciu o oczekiwany minimalny współczynnik kompresji.

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); 

Jeśli nieskompresowany plik jest większy niż 4294967296 * 2, a następnie będziesz musiał odgadnąć, jak wiele wielokrotności 4294967296 zastosować (chociaż nigdy nie przetestowane), jednak trzeba mieć dokładne sędzia oczekiwanego stopnia sprężania do tego, aby wypracować:

my $estimated_multiplier = int(($outerbytes * MIN_COMPRESS_FACTOR)/4294967296); 
$bytes += (4294967296 * $estimated_multiplier) if($estimated_multiplier); 
Powiązane problemy