Использование неинициализированного значения Ошибка в Perl при использовании аргументов больше 43371

У меня есть файл (filename.txt), который находится в текущем рабочем каталоге. Формат каждой строки:

ID name ancestors

Пользователь вводит 2 или более идентификатора в аргументе командной строки. Например:

perl program.pl 6 13

и он получит предков из 6 и 13 и распечатает общих предков. (бактерия)

Моя программа работает для всех идентификаторов до ID 43371. Если я ввожу идентификаторы, превышающие 43371, это не работает и дает мне эту ошибку:

Use of uninitialized value $len in substr at test4.pl line 28, <file> line 48542.
Use of uninitialized value in substr at test4.pl line 28, <file> line 48542.
</file></file>

строка 28:

print substr $anc[0], 0, $len;

Вот мой код:

#!/usr/bin/perl
use strict;
use warnings;

my $a;
my @chunks;
my @anc;
my $temp = '';
my $len;
my $string;
open FILE, "filename.txt";

foreach $a(0 .. $#ARGV){
 while (my $line = <file>){
 @chunks = split(/\t/, $line);
 chomp $chunks[0];
 if ($ARGV[$a] == $chunks[0]){
 push (@anc, $chunks[3]); #stored at chunk[3] because one area is separated by 2 tabs 
 last;
 }
 }
 seek FILE, 0, 0;
}
$temp ^= $_ for @anc;
$temp ^= $anc[0] if @anc &1;
$temp =~ m[^(\0+)];
$len = length($1);
print substr $anc[0], 0, $len;
print "\n";
close FILE;
</file>

первые несколько строк filename.txt

1 root other sequences
2 Bacteria ********** Bacteria
6 Azorhizobium Bacteria; Proteobacteria; Alphaproteobacteria; Rhizobiales; Xanthobacteraceae
7 Azorhizobium caulinodans Bacteria; Proteobacteria; Alphaproteobacteria; Rhizobiales; Xanthobacteraceae; Azorhizobium
9 Buchnera aphidicola Bacteria; Proteobacteria; Gammaproteobacteria; Enterobacteriales; Enterobacteriaceae; Buchnera
10 Cellvibrio Bacteria; Proteobacteria; Gammaproteobacteria; Pseudomonadales; Pseudomonadaceae
11 Cellvibrio gilvus Bacteria; Proteobacteria; Gammaproteobacteria; Pseudomonadales; Pseudomonadaceae; Cellvibrio
13 Dictyoglomus Bacteria; Dictyoglomi; Dictyoglomales; Dictyoglomaceae

Изменить - я попробовал запустить его с тем же кодом, который опубликовал Миллер:

#!/usr/bin/perl
use strict;
use warnings;
use autodie;

open my $fh, "taxonomy.txt";

my @anc;

while (<$fh>){
 chomp;
 my @chunks = split /\t/;
 if (grep {$_ == $chunks[0]} @ARGV){
 push @anc, $chunks[3];
 }
}
my $temp = '';
$temp ^= $_ for @anc;
$temp ^= $anc[0] if @anc &1;
if ($temp =~ m[^(\0+)]) {
 my $len = length($1);
 print substr $anc[0], 0, $len;
 print "\n";
}
else {
 warn "Did not find match:(";
}
close $fh;

Входные данные:

perl test5.pl 62763 66968

Вывод:

Did not find match:( at test5.pl line 26, <$_[...]> line 24271.

Выход должен быть:

Bacteria; Proteobacteria; Gammaproteobacteria;

Данные:

62763 Vibrio pectenicida Bacteria; Proteobacteria; Gammaproteobacteria; Vibrionales; Vibrionaceae; Vibrio

66968 Legionella sp. J Bacteria; Proteobacteria; Gammaproteobacteria; Legionellales; Legionellaceae; Legionella
1 ответ

Эта часть проблематична, поскольку невозможно определить $1:

$temp =~ m[^(\0+)];
$len = length($1);
print substr $anc[0], 0, $len;

Всегда проверяйте соответствие регулярного выражения, прежде чем пытаться использовать захваченную переменную:

if ($temp =~ m[^(\0+)]) {
 $len = length($1);
 print substr $anc[0], 0, $len;
} else {
 warn "Didn't match :(";
}

Для дополнительного кредита я бы посоветовал вам следовать рекомендациям TLP и ограничить сферу охвата всех ваших переменных. Выполнение этого и немало других улучшений приводит к следующему коду, который в основном не тестировался:

#!/usr/bin/perl
use strict;
use warnings;
use autodie;

open my $fh, "filename.txt";

my @anc;

while (<$fh>){
 chomp;
 my @chunks = split /\t/;
 if (grep {$_ == $chunks[0]} @ARGV){
 # Let output some debugging info and make sure things work as intended.
 print "$chunks[0] - $chunks[3]\n";
 push @anc, $chunks[3]; #stored at chunk[3] because one area is separated by 2 tabs 
 }
}

close $fh;

my $intersection = shift @anc;
for (@anc) {
 my $overlap = $intersection ^ $_;
 $overlap =~ m/^(\0*)/;
 $intersection = substr $intersection, 0, length $1;
}

print "$intersection\n";

Обновить

В качестве окончательной версии вашего очищенного кода пересечения я создал следующее:

my $intersection = shift @anc;
for (@anc) {
 my $overlap = $intersection ^ $_;
 $overlap =~ m/^(\0*)/;
 $intersection = substr $intersection, 0, length $1;
}

Обратите внимание, что ключевое различие заключается в том, что в regex используется * вместо + чтобы он всегда соответствовал.

licensed under cc by-sa 3.0 with attribution.