Perl читает только определенные строки файла gz

Я пытаюсь создать скрипт синтаксического анализа, который анализирует огромный текстовый файл (более 2 миллионов строк), сжатый с помощью gunzip. Я только хочу проанализировать диапазон строк в текстовом файле. До сих пор я использовал zgrep -n, чтобы найти две строки, в которых упоминается строка, которая, как я знаю, будет начинать и заканчивать интересующий меня раздел файла.

В моем файле тестового примера меня интересует только чтение в строках с 123080 по 139361. Я нашел Tie::File для доступа к строкам файла с помощью возвращаемого объекта массива, но, к сожалению, это не сработает для файла zip-пистолета. Я работаю с.

Есть ли что-то вроде следующего для заархивированного файла?

use Tie::File
tie @fileLinesArray, 'Tie::File', "hugeFile.txt.gz"
my $startLine = 123080;

my $endLine = 139361;    
my $lineCount = $startLine;
while ($lineCount <= $endLine){
    my $line = @fileLinesArray[$lineCount]
    blah blah...
}

3 ответа

Решение

Используйте http://p3rl.org/IO::Uncompress::Gunzip, который является основным модулем:

use IO::Uncompress::Gunzip;

my $z = IO::Uncompress::Gunzip->new('file.gz');
$z->getline for 1 .. $start_line - 1;
for ($start_line .. $end_line) {
    my $line = $z->getline;
    ...
}

Tie:: File становится очень медленным и требует много памяти при обработке больших файлов.

Tie::File плохая идея для больших файлов, так как нужно хранить весь файл в памяти сразу. Это также непрактичная, если не невозможная идея для сжатых файлов. Вместо этого вы захотите работать с потоком ввода ваших данных. И если вы собираетесь изменить данные, выходной поток в новую копию данных. Perl имеет довольно хорошую поддержку сжатия gzip через PerlIO::gzip' layer, but you could also pipe data through one or twoгзип` процессы.

# I/O stream initialization
use PerlIO::gzip;
open my $input, "<:gzip", "data.gz";
open my $output. ">:gzip", "data.new.gz";    # if $output is needed

# I/O stream initialization without PerlIO::gzip
open my $input, "gzip -d data.gz |";
open my $output, "| gzip -c > data.new.gz";

После настройки входных (и необязательных выходных) потоков вы можете использовать в них средства ввода / вывода Perl, как и любые другие файловые дескрипторы.

# copy first $startLine lines unedited
while (<$input>) {
    print $output $_;
    last if $. >= $startLine;
}

while (my $line = <$input>) {
    # blah blah blah
    # manipulate $line
    print $output $line;
    last if $. >= $endLine;
}

print $output <$input>; # write remaining input to output stream
close $input;
close $output;

Вы пишете: "В моем файле теста я интересуюсь только чтением в строках с 123080 по 139361".

Это можно сделать и в оболочке:

zcat file | tail -n +123080 | head -16282

Или:

my $file = 'the_file.gz';
my($from,$to) = (123080,139361);
my @lines = qx( zcat $file | tail -n +$from | head -@{[-$from+$to+1]});

Это может быть быстрее, чем обычное одноядерное чистое решение Perl, так как zcat, tail а также head внутри qx станет три процесса, а perl - четвертый. И все четыре могут получить отдельное ядро ​​процессора самостоятельно. Возможно, вы захотите проверить скорость с разными номерами строк.

Другие вопросы по тегам