Perl `(?PARNO)` сбрасывает свои собственные именованные захваты, когда это сделано?
Понимают ли рекурсивные регулярные выражения именованные захваты? В документах есть примечание для (?{{ code }})
что это независимый подшаблон с собственным набором перехватов, которые отбрасываются, когда подшаблон делается, и есть примечание в (?PARNO)
что его "похоже на (?{{ code }})
, Является (?PARNO)
отменить свои собственные именованные захваты, когда это будет сделано?
Я пишу о рекурсивных регулярных выражениях Perl для Mastering Perl. У perlre уже есть пример со сбалансированными паренами (я показываю его в Соответствии сбалансированным круглым скобкам в регулярном выражении Perl), поэтому я решил попробовать сбалансированные кавычки:
#!/usr/bin/perl
# quotes-nested.pl
use v5.10;
$_ =<<'HERE';
He said 'Amelia said "I am a camel"'
HERE
say "Matched!" if m/
(
['"]
(
(?:
[^'"]+
|
( (?1) )
)*
)
['"]
)
/xg;
print "
1 => $1
2 => $2
3 => $3
4 => $4
5 => $5
";
Это работает, и две цитаты появляются в $1
а также $3
:
Matched!
1 => 'Amelia said "I am a camel"'
2 => Amelia said "I am a camel"
3 => "I am a camel"
4 =>
5 =>
Все в порядке. Я это понимаю. Однако я не хочу знать цифры. Итак, я делаю первую группу захвата именованным захватом и смотрю в %-
ожидая увидеть две подстроки, которые я ранее видел в $1
а также $2
:
use v5.10;
$_ =<<'HERE';
He said 'Amelia said "I am a camel"'
HERE
say "Matched [$+{said}]!" if m/
(?<said>
['"]
(
(?:
[^'"]+
|
(?1)
)*
)
['"]
)
/xg;
use Data::Dumper;
print Dumper( \%- );
Я вижу только первое:
Matched ['Amelia said "I am a camel"']!
$VAR1 = {
'said' => [
'\'Amelia said "I am a camel"\''
]
};
Я ожидал что (?1)
будет повторять все в первой группе захвата, в том числе именованный захват said
, Я могу это исправить, назвав новый снимок:
use v5.10;
$_ =<<'HERE';
He said 'Amelia said "I am a camel"'
HERE
say "Matched [$+{said}]!" if m/
(?<said>
['"]
(
(?:
[^'"]+
|
(?<said> (?1) )
)*
)
['"]
)
/xg;
use Data::Dumper;
print Dumper( \%- );
Теперь я получаю то, что ожидал:
Matched ['Amelia said "I am a camel"']!
$VAR1 = {
'said' => [
'\'Amelia said "I am a camel"\'',
'"I am a camel"'
]
};
Я думал, что смогу это исправить, переместив именованный захват на один уровень вверх:
use v5.10;
$_ =<<'HERE';
He said 'Amelia said "I am a camel"'
HERE
say "Matched [$+{said}]!" if m/
(
(?<said>
['"]
(
(?:
[^'"]+
|
(?1)
)*
)
['"]
)
)
/xg;
use Data::Dumper;
print Dumper( \%- );
Но это не ловит меньшую подстроку в said
или:
Matched ['Amelia said "I am a camel"']!
$VAR1 = {
'said' => [
'\'Amelia said "I am a camel"\''
]
};
Я думаю, что понимаю это, но я также знаю, что здесь есть люди, которые на самом деле касаются кода C, который делает это возможным.:)
И, когда я пишу это, я думаю, что я должен перегружать связь STORE для %-
чтобы узнать, но тогда я должен был бы выяснить, как это сделать.
1 ответ
Поиграв с этим, я удовлетворен тем, что то, что я сказал в вопросе, правильно. Каждый звонок (?PARNO)
получает полный и отдельный набор переменных соответствия, которые он отбрасывает в конце своего запуска.
Вы можете получить все вещи, которые совпадают в каждом подшабле, используя массив, внешний по отношению к оператору сопоставления с образцом, и добавив его в конец повторяющегося подшаблона, как в этом примере:
#!/usr/bin/perl
# nested_carat_n.pl
use v5.10;
$_ =<<'HERE';
Outside "Top Level 'Middle Level "Bottom Level" Middle' Outside"
HERE
my @matches;
say "Matched!" if m/
(?(DEFINE)
(?<QUOTE_MARK> ['"])
(?<NOT_QUOTE_MARK> [^'"])
)
(
(?<quote>(?"E_MARK))
(?:
(?&NOT_QUOTE_MARK)++
|
(?R)
)*
\g{quote}
)
(?{ push @matches, $^N })
/x;
say join "\n", @matches;
Я подробно рассмотрю это в главе 2 "Освоение Perl", которую вы можете прочитать бесплатно (по крайней мере, на некоторое время).