32 votes

Est-ce que la fonction `(?PARNO)` de Perl rejette ses propres captures nommées lorsqu'elle a terminé ?

Les regex récursifs comprennent-ils les captures nommées ? Il y a une note dans les docs pour (?{{ code }}) qu'il s'agit d'un sous-modèle indépendant avec son propre ensemble de captures qui sont éliminées lorsque le sous-modèle est terminé. (?PARNO) que sa "similarité avec (?{{ code }}) . Est (?PARNO) en jetant ses propres captures nommées quand il a fini ?

J'écris sur les expressions régulières récursives de Perl pour Maîtriser Perl . perlre a déjà un exemple avec des parens équilibrés (je le montre dans Correspondance des parenthèses équilibrées dans les regex Perl ), alors je me suis dit que j'allais essayer les guillemets équilibrés :

#!/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
";

Cela fonctionne et les deux citations apparaissent dans $1 y $3 :

Matched!
1 => 'Amelia said "I am a camel"'
2 => Amelia said "I am a camel"
3 => "I am a camel"
4 => 
5 => 

C'est bien. Je le comprends. Cependant, je ne veux pas connaître les chiffres. Donc, je fais du premier groupe de capture une capture nommée et je regarde dans %- s'attendant à voir les deux sous-chaînes que j'ai vues précédemment dans $1 y $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( \%- );

Je ne vois que le premier :

Matched ['Amelia said "I am a camel"']!
$VAR1 = {
          'said' => [
                      '\'Amelia said "I am a camel"\''
                    ]
        };

Je m'attendais à ce que (?1) répéterait tout ce qui se trouve dans le premier groupe de capture, y compris la capture nommée à said . Je peux corriger un peu cela en nommant une nouvelle capture :

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( \%- );

Maintenant, j'obtiens ce que j'attendais :

Matched ['Amelia said "I am a camel"']!
$VAR1 = {
          'said' => [
                      '\'Amelia said "I am a camel"\'',
                      '"I am a camel"'
                    ]
        };

J'ai pensé que je pourrais résoudre ce problème en déplaçant la capture nommée vers le haut d'un niveau :

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( \%- );

Mais, cela n'attrape pas la plus petite sous-chaîne dans said soit :

Matched ['Amelia said "I am a camel"']!
$VAR1 = {
          'said' => [
                      '\'Amelia said "I am a camel"\''
                    ]
        };

Je pense avoir compris, mais je sais aussi qu'il y a des gens ici qui touchent au code C qui permet de le faire :)

Et, alors que j'écris ceci, je pense que je devrais surcharger la cravate STORE pour %- pour le découvrir, mais alors je devrais trouver comment le faire.

4voto

brian d foy Points 71781

Après avoir joué avec cela, je suis convaincu que ce que j'ai dit dans la question est correct. Chaque appel à (?PARNO) obtient un ensemble complet et séparé des variables de correspondance qu'il rejette à la fin de son exécution.

Vous pouvez obtenir tous les éléments qui correspondent à chaque sous-motif en utilisant un tableau externe à l'opérateur de correspondance de motifs et en le poussant à la fin du sous-motif répété, comme dans cet exemple :

#!/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;

Je l'aborde en profondeur dans Chapitre 2 de Mastering Perl que vous pouvez lire gratuitement (du moins pendant un certain temps).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X