У меня очень простой код, который анализирует имя файла:
#!/usr/bin/env perl
use 5.040;
use warnings FATAL => 'all';
use autodie ':default';
my $string = '/home/con/bio.data/blastdb/phytophthora.infestans.KR_2_A2/GCA_012552325.1.protein.faa';
if ($string =~ m/blastdb/(\w)\w+\.([\w\.]+)/) {
my $rest = $2; # $1 would be valid here
$rest =~ s/\./ /g;
my $name = "$1.$rest"; # $1 disappears here
}
приведенный выше код не работает с Use of uninitialized value $1 in concatenation (.) or string
Однако, если я сохраню $1
в переменную, например. $g
, информация не теряется.
if ($string =~ m/blastdb/(\w)\w+\.([\w\.]+)/) {
my ($g, $rest) = ($1, $2);
$rest =~ s/\./ /g;
my $name = "$g.$rest";
}
Так что я могу это исправить.
Однако $1
не должен просто так исчезнуть, разве $1
не должен оставаться действительным, пока находится в области видимости? Это ошибка в Perl? или есть какое-то правило в https://perldoc.perl.org/perlretut, которое я пропустил?
$rest =~ s/\./ /g;
выполняет совпадение с регулярным выражением. Шаблон, которому он соответствует (/\./
), не имеет групп захвата, поэтому все переменные захвата после его завершения не инициализируются.
Вы можете сохранить то, что вам нужно, в переменных — проще всего, выполнив if (my ($g, $rest) = $string =~ /yadda yadda/)
, или вы можете избежать выполнения других сопоставлений с регулярными выражениями до того, как вы закончите с захватами из предыдущего — в этом случае $rest =~ tr/./ /
справится с этой задачей так же хорошо, как $rest =~ s/\./ /g
, но без уничтожения переменных захвата.
И если бы у s///
были группы захвата, переменные захвата были бы сброшены до тех значений, которые они захватили.
Каждое успешное совпадение сбрасывает переменные совпадения. См. здесь Еще больше сбивает с толку то, что переменные сбрасываются только для совпадений - источник многих укусов...