2016-11-03 2 views
2

У нас есть много сжатых данных, которые на самом деле представляют собой сжатый записанный в архив архив каталогов и его подкаталогов, содержащих XML-файлы; напримерМожете ли вы передать файл в файл, строка за строку из архива .tar.bz2 в Perl?

omega/  
- alpha/ 
    - a/ 
    - file1.xml 
    - file2.xml 
    - file3.xml 
    - b/ 
    - file1.xml 
    - file2.xml 
    - file3.xml 
    - c/ 
    - ... 
- beta/ 
    - a/ 
    - file1.xml 
    - file2.xml 
    - file3.xml 
    - b/ 
    - ... 
    - c/ 
    - ... 
- gamma/ 
    - a/ 
    - ... 
    - b/ 
    - ... 
    - c/ 
    - ... 

В результате были бы такие файлы, как omega.tar.bz2 и эти файлы могут достигать размера в сотни гигабайт.

Несмотря на то, что мы знаем, что это файл , было бы неплохо по-прежнему использовать его содержимое, когда нам нужно. Поэтому мне было интересно, можно ли читать из этих файлов в Perl потоковым образом, то есть без предварительной распаковки и распаковки всего на диске или без необходимости загружать файл *.tar.bz2 в память.

Я знаю, что с IO::Uncompress вы можете Bunzip2, но насколько я могу видеть и протестировать, это будет читать весь файл в памяти, который невозможен с нашими большими файлами. Пример кода ниже для Bunzipping (не включая TAR).

use strict; 
use warnings; 
use IO::Uncompress::Bunzip2 qw(bunzip2 $Bunzip2Error) ; 

my $filename = '/path/to/file/file1.xml.bz2'; 
open(my $fh, '<', $filename) 
    or die "Could not open file '$filename' $!"; 

my $buffer ; 
bunzip2 $filename => \$buffer 
    or die "bunzip2 failed: $Bunzip2Error\n"; 

print STDOUT "$buffer\n"; 

Принимая TAR во внимание, есть также Archive::Extract модуль, который позволяет читать .tar.bz2 файл (типа tbz) в качестве Extract Object, но опять-таки это прочитал бы весь файл в память, которая не возможна с нашими ginormous файлы.

Из-за моих собственных исследований в этой теме я думаю, что маловероятно, чтобы можно было читать TAR BZIP2s потоковым способом, т. Е. Линию на линию. Однако у меня нет опыта сжатия, так что, возможно, есть способ восстановить строки файлов, учитывая количество блоков данных.

Tl; dr: можете ли вы передать содержимое файла (строка-строка или подобное) из сжатого TAR-архива BZIP2?

ответ

2

Существует Compress::Raw::Bzip2, который позволяет вам распаковать входной блок bzip2 куском, то есть в потоке. Но так как .tz.bz2 - это сначала tar-файл, который затем сжимается с помощью bzip2, вам нужно сначала распаковать все данные до расположения файлов в файле tar до того, как вы получите доступ к нужным вам данным, то есть нет способа искать файл без декомпрессии, вплоть до этого файла. Если у вас все в порядке, вы можете использовать Archive::Tar::Stream, т. Е. Подавать входной сигнал от вашего декодера bzip2 в потоковый анализатор Tar. Я никогда не использовал его сам, но похоже, что он был разработан именно для такого варианта использования.

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

+0

Благодарим за информацию. Но если вы используете ZIP, разве весь ZIP не должен быть распакован до того, как любой из сжатых файлов будет доступен? Будет ли это похоже на первый bzipping всех файлов и * then * переносить их? –

+0

@BramVanroy: с ZIP файлы сжимаются внутри архива, т. Е. Можно искать конкретный файл, а затем распаковывать его, а не распаковывать все в архиве до этого файла. –

0

Все модули ввода-вывода IO :: Compress и IO :: Uncompress поддерживают потоковое вещание, включая IO :: Uncompress :: Bunzip2. Пример кода, который вы показали (см. Ниже), использует метод удобства (bunzip2) для общего использования, где вы хотите прочитать все сжатые данные из файла и разогнать его в буфер за один раз.

my $buffer ; 
bunzip2 $filename => \$buffer 
    or die "bunzip2 failed: $Bunzip2Error\n"; 

Вот использование для потокового bunzip2 прецеденты

my $bz = IO::Uncompress::Bunzip2->new($filename); 

# $bz is a regular Perl filehandle, so can read it a line at a time 
while (<$bz>) 
{ 
    .... 
} 

# or a bock at a time 
read($bz, $buffer, 1024); 

close $gz; 

Если вы можете найти модуль дегтя, который принимает PERL дескриптор и сам по себе является потоковой вы можете дать ему IO :: распаковывать :: Объект Bubzip2.

Другой вариант - просто дать «real» бинарный дескриптор tar для этого. Более новая версия gnu tar автоматически обнаружит сжатие, и вы можете получить tar для записи в stdout. Таким образом, вы можете просто открыть дескриптор файла для команды tar, например,

open my $data, "tar -Of $file.tar.bz2 |"; 

while (<$data>) 
{ 
    .... 
} 
Смежные вопросы