2011-02-09 30 views
6

Tôi có một loạt các tệp lớn vô cùng (kích thước nhiều gigabyte) có tỷ lệ nén thực sự cao (1: 200 hoặc cao hơn). Tôi phải xử lý chúng và muốn ít nhất là cho thấy một số loại ước tính tiến độ. Vì lý do đó tôi muốn biết kích thước của tập tin bên trong .gz, vì vậy tôi có thể so sánh nó với những gì tôi đã rút ra rồi.Làm thế nào để tôi nói trong Perl kích thước của một tệp trong kho lưu trữ gzip là gì mà không giải nén toàn bộ tệp?

Tuy nhiên, vì việc giải nén toàn bộ tệp trước mỗi lần là khá nghiêm trọng và lãng phí thời gian, tôi muốn tìm ra kích thước mà không làm điều đó.

Tôi biết điều đó là có thể. Tôi chỉ có thể mở tập tin gzip với Total Commander và plugin người xem sẽ cho tôi thấy kích thước phù hợp. (Tôi biết nó không giải nén vì nó cho tôi thấy kích thước ngay lập tức, điều này thực sự không thể thực hiện được với một tệp 10 GB bên trong gzip.)

Có thể có một số trường tiêu đề chứa thông tin đó.

Tuy nhiên, tìm kiếm tài liệu của các mô-đun CPAN khác nhau mà tôi không thể tìm thấy bất kỳ thứ gì phù hợp với hóa đơn. IO::Uncompress::Gunzip cho phép tôi nhận được tại tiêu đề nhưng không chứa bất kỳ thông tin kích thước tệp nào.

Mọi đề xuất?

+3

Bạn đúng - có trường ISIZE, xem tại đây để biết chi tiết: http://www.gzip.org/zlib/rfc- gzip.html # header-trailer –

+0

Hmm, vì vậy tôi đoán trừ khi có API Perl cho điều đó, truy vấn duy nhất của tôi là đọc bốn byte cuối cùng của tệp theo cách thủ công? – Mithaldu

+7

Giải pháp nhanh chóng và bẩn sẽ phân tích đầu ra của 'gzip --list'. – Ether

Trả lời

1

Chỉ cần để có một câu trả lời thích hợp cho việc này:

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

Tác vụ này, nhưng gợi lên lệnh gzip, thay vì đào sâu vào tệp gzip để trích xuất 4 byte cuối cùng. – ChuckCottrill

+0

Gọi gzip là tùy chọn tốt hơn, vì nó tránh sự trùng lặp mã. Mục tiêu ở đây là để tránh giải nén toàn bộ điều, không phải để tránh gzip. – Mithaldu

1

Như đã trình bày trong các ý kiến ​​trên, 4 byte cuối cùng chứa iSize

Dưới đây là một số mã tôi đã viết để tính toán các byte không nén được cung cấp đường dẫn tệp:

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

Đối với các tệp không nén lớn hơn 4Gb, bạn sẽ cần đoán có thêm 4Gb vào thứ e isize được truy xuất, dựa trên hệ số nén tối thiểu mong đợi.

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

Nếu tập tin nén của bạn lớn hơn 4294967296 * 2, sau đó bạn sẽ phải đoán có bao nhiêu bội số của 4294967296 để áp dụng (mặc dù tôi chưa bao giờ được thử nghiệm này), tuy nhiên bạn sẽ cần phải có một phán đoán chính xác về tỷ lệ nén dự kiến ​​để làm việc này:

my $estimated_multiplier = int(($outerbytes * MIN_COMPRESS_FACTOR)/4294967296); 
$bytes += (4294967296 * $estimated_multiplier) if($estimated_multiplier); 
Các vấn đề liên quan