Posso usare le espressioni regolari di Perl per abbinare il testo bilanciato?

Vorrei abbinare il testo racchiuso tra parentesi ecc. In Perl. Come lo posso fare?


Questa è una domanda dal perlfaq ufficiale . Stiamo importando il perlfaq su Stack Overflow .

Questa è la risposta ufficiale alle domande frequenti meno eventuali modifiche successive.

Il tuo primo tentativo dovrebbe probabilmente essere il modulo Text :: Balanced , che si trova nella libreria standard Perl da Perl 5.8. Ha una varietà di funzioni per gestire il testo difficile. Il modulo Regexp :: Common può anche aiutare fornendo schemi predefiniti che è ansible utilizzare.

A partire da Perl 5.10, è ansible abbinare il testo bilanciato con le espressioni regolari usando schemi ricorsivi. Prima di Perl 5.10, dovevi ricorrere a vari trucchi come l’uso del codice Perl nelle sequenze (??{}) .

Ecco un esempio che utilizza un’espressione regolare ricorsiva. L’objective è quello di catturare tutto il testo tra parentesi angolari, incluso il testo in parentesi angolari annidate. Questo testo di esempio ha due gruppi “principali”: un gruppo con un livello di nidificazione e un gruppo con due livelli di nidificazione. Ci sono cinque gruppi totali tra parentesi angolari:

 I have some  > and  > > and that's it. 

L’espressione regolare per abbinare il testo bilanciato utilizza due nuove (per il 5.10) caratteristiche di espressioni regolari. Questi sono trattati in perlre e questo esempio è una versione modificata di uno in quella documentazione.

Innanzitutto, aggiungere il nuovo + possessivo a qualsiasi quantificatore trova la corrispondenza più lunga e non torna indietro. Questo è importante dal momento che si desidera gestire qualsiasi parentesi angular attraverso la ricorsione, non il backtracking. Il gruppo [^<>]++ trova una o più parentesi senza angolo senza retrocedere.

In secondo luogo, il nuovo (?PARNO) riferisce al sub-pattern nel particolare gruppo di cattura dato da PARNO . Nella regex seguente, il primo gruppo di cattura trova (e ricorda) il testo bilanciato, e serve lo stesso modello nel primo buffer per superare il testo annidato. Questa è la parte ricorsiva. Il (?1) usa il modello nel gruppo di cattura esterno come parte indipendente della regex.

Mettendo tutto insieme, hai:

 #!/usr/local/bin/perl5.10.0 my $string =<<"HERE"; I have some  > and  > > and that's it. HERE my @groups = $string =~ m/ ( # start of capture group 1 < # match an opening angle bracket (?: [^<>]++ # one or more non angle brackets, non backtracking | (?1) # found < or >, so recurse to capture group 1 )* > # match a closing angle bracket ) # end of capture group 1 /xg; $" = "\n\t"; print "Found:\n\[email protected]\n"; 

L’output mostra che Perl ha trovato i due gruppi principali:

 Found:  >  > > 

Con un po ‘di lavoro in più, puoi ottenere tutti i gruppi in parentesi angolari anche se si trovano in altre parentesi angolari. Ogni volta che ottieni una corrispondenza bilanciata, rimuovi il suo delimitatore esterno (quello che hai appena abbinato quindi non lo abbina di nuovo) e aggiungilo a una coda di stringhe da elaborare. Continua a farlo finché non ottieni nessuna corrispondenza:

 #!/usr/local/bin/perl5.10.0 my @queue =<<"HERE"; I have some  > and  > > and that's it. HERE my $regex = qr/ ( # start of bracket 1 < # match an opening angle bracket (?: [^<>]++ # one or more non angle brackets, non backtracking | (?1) # recurse to bracket 1 )* > # match a closing angle bracket ) # end of bracket 1 /x; $" = "\n\t"; while( @queue ) { my $string = shift @queue; my @groups = $string =~ m/$regex/g; print "Found:\n\[email protected]\n\n" if @groups; unshift @queue, map { s/^$//; $_ } @groups; } 

L’output mostra tutti i gruppi. Le partite più esterne vengono mostrate per prime e le partite annidate vengono visualizzate più tardi:

 Found:  >  > > Found:  Found:  > Found: