Perl манипулирует большими файлами

Я работаю над файлом 16 ГБ и небольшим файлом.

Я пытался загрузить оба файла в память. Затем я переместился на каждую строку в большом файле и проверил что-то в маленьком файле (для каждой строки в большом файле, который я перебрал в маленьком).

Это мой код

local $/ = undef;
open my $fh1, '<', $in or die "error opening $in: $!";
my $input_file = do { local $/; <$fh1> };

local $/ = undef;
open my $fh2, '<', $handle or die "error opening $handle: $!";
my $handle_file = do { local $/; <$fh2> };

my $counter_yes = 0;
my $counter_no  = 0;
my $flag        = 0;

my @lines1 = split /\n/, $input_file;

foreach my $line( @lines1 ) {

    my @f = split('\t', $line); # $f[0] and $f[1]
    print "f0 and f1 are: $f[0] and $f[1]\n";
    my @lines2 = split /\n/, $handle_file;

    foreach my $input ( @lines2 ){

        #print "line2 is: $input\n";
        my @sp = split /:/, $input; # $sp[0] and $sp[1]

        if ( $sp[0] eq $f[0] ){

            my @r = split /-/, $sp[1];

            if ( ($f[1] >= $r[0]) && ($f[1] <= $r[1]) ){
                $flag = 1;
                $counter_yes = $counter_yes;
                last;
            }
        }
    }

    if ( $flag == 0 ){
        $counter_no = $counter_no  ;
    }
}

Пока я его запускаю, я получаю ошибку

Split loop at script.pl line 30, <$fh2> chunk 1

В чем может быть причина?

3 ответа

Вы можете запустить perldoc perldiag узнать, что означают некоторые встроенные ошибки и предупреждения.

   Split loop
       (P) The split was looping infinitely.  (Obviously, a split
       shouldn't iterate more times than there are characters of input,
       which is what happened.)  See "split" in perlfunc.

Строка, на которую вы разбиваете, настолько велика, что Перл думал, что она повторяется бесконечно. Когда Perl разделяет строку больше, чем длина строки + 10, он выдает эту ошибку, принимая ее в бесконечном цикле. К сожалению для вас, он сохранил это число как 32-разрядное целое число, которое может содержать до 2 миллиардов и меняться. Ваша строка превышает 16 миллиардов, поэтому результат будет непредсказуемым.

Это было недавно исправлено в 5.20 вместе со многими другими проблемами, связанными с работой со строками размером более 2G. Поэтому, если вы обновите Perl, ваш код будет "работать".

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

my @small_data = <$small_fh>;
chomp @small_data;

while( my $big = <$big_fh> ) {
    chomp $big;

    for my $small (@small_data) {
        ...
    }
}

Но даже это будет ужасно неэффективно, если ваш маленький файл содержит 1000 строк, тогда этот цикл будет выполняться 16 триллионов раз!

Поскольку кажется, что вы проверяете, находятся ли записи в большом файле в маленьком файле, вам лучше превратить записи в маленьком файле в хеш-таблицу.

my %fields;
while( my $line = <$small_fh> ) {
    chomp $line;
    my @sp = split /:/, $line;
    $fields{$sp[0]} = $sp[1];
}

Теперь вы можете перебирать большой файл и просто выполнять поиск по хешу.

while( my $line = <$big_fh> ) {
    chomp $line;
    my @f = split('\t', $line);

    if( defined $fields{$f[0]} ) {
        ...
    }
}

Почему вы читаете весь файл в одну большую строку и разбиваете его на массив строк, когда вы могли бы начать с чтения массива строк? И почему вы делаете это снова и снова для второго файла? Вы можете просто

chomp(my @lines1 = <$fh>);
chomp(my @lines2 = <$fh2>);

в верхней части вашей программы и устранить $input_file а также $handle_file которые в противном случае не используются, и все $/ ерунда. Это вполне может быть источником проблемы, поскольку сообщение об ошибке указывает, что разделение создает "слишком много" полей.

Я работаю над файлом 16 ГБ и небольшим файлом.

Я пытался загрузить оба файла в память.

У вас есть 16 ГБ памяти? На самом деле, ваш код требует более 32 ГБ памяти.

Разделение цикла в строке script.pl 30, чанк 1

Я не могу дублировать эту ошибку. Ошибки Perl обычно довольно наглядны, но это даже не понятно.

Далее, если это было в вашем коде:

my $x = 10;
#nothing changes $x
#in these
#lines
$x = 10;

Какова будет цель последней строки? Тем не менее, вы сделали это:

$/ = undef;
#Nothing changes $/
#in these lines
$/ = undef;

Далее все Perl-программы должны начинаться со следующих строк:

<guess>

Если вы не знаете, то вам нужно купить начинающую книгу по Perl.

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