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>(?&QUOTE_MARK))
        (?:
            (?&NOT_QUOTE_MARK)++
            |
            (?R)
        )*
    \g{quote}
    )
    (?{ push @matches, $^N })
    /x;

say join "\n", @matches;

Я подробно рассмотрю это в главе 2 "Освоение Perl", которую вы можете прочитать бесплатно (по крайней мере, на некоторое время).

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