2010-12-15 16 views

Respuesta

22

Este es el official FAQ answer menos las ediciones posteriores.

Su primer intento probablemente sea el módulo Text::Balanced, que se encuentra en la biblioteca estándar de Perl desde Perl 5.8. Tiene una variedad de funciones para tratar con texto complicado. El módulo Regexp::Common también puede ayudar al proporcionar patrones enlatados que puede usar.

A partir de Perl 5.10, puede hacer coincidir texto equilibrado con expresiones regulares utilizando patrones recursivos. Antes de Perl 5.10, tenías que recurrir a varios trucos, como usar el código Perl en las secuencias (??{}).

Aquí hay un ejemplo que usa una expresión regular recursiva. El objetivo es capturar todo el texto entre corchetes angulares, incluido el texto en corchetes angulares anidados. Este texto de muestra tiene dos grupos "principales": un grupo con un nivel de anidación y un grupo con dos niveles de anidación. Hay cinco grupos totales en corchetes angulares:

I have some <brackets in <nested brackets> > and 
<another group <nested once <nested twice> > > 
and that's it. 

La expresión regular para que coincida con el texto equilibrado utiliza dos nuevas (a Perl 5.10) características de expresiones regulares. Estos están cubiertos en perlre y este ejemplo es una versión modificada de uno en esa documentación.

Primero, al agregar el nuevo posesivo + a cualquier cuantificador encuentra la coincidencia más larga y no retrocede. Eso es importante ya que desea manejar cualquier paréntesis angulares a través de la recursión, no retroceder. El grupo [^<>]++ encuentra uno o más corchetes no angulares sin retroceder.

En segundo lugar, el nuevo (?PARNO) se refiere al subpatrón en el grupo de captura particular dado por PARNO. En la siguiente expresión regular, el primer grupo de captura encuentra (y recuerda) el texto balanceado, y necesita ese mismo patrón dentro del primer búfer para pasar el texto anidado. Esa es la parte recursiva. El (?1) usa el patrón en el grupo de captura externo como una parte independiente de la expresión regular.

Poniendo todo junto, usted tiene:

#!/usr/local/bin/perl5.10.0 

my $string =<<"HERE"; 
I have some <brackets in <nested brackets> > and 
<another group <nested once <nested twice> > > 
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"; 

El resultado muestra que Perl encontró a los dos grupos principales:

Found: 
    <brackets in <nested brackets> > 
    <another group <nested once <nested twice> > > 

Con un poco de trabajo extra, se puede obtener la totalidad de la grupos en paréntesis angulares incluso si también están en otros paréntesis angulares. Cada vez que obtenga una coincidencia equilibrada, elimine su delimitador externo (ese es el que acaba de emparejar, así que no lo vuelva a hacer) y agréguelo a una cola de cadenas para procesar. Siga haciéndolo hasta que no obtenga coincidencias:

#!/usr/local/bin/perl5.10.0 

my @queue =<<"HERE"; 
I have some <brackets in <nested brackets> > and 
<another group <nested once <nested twice> > > 
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/^<//; s/>$//; $_ } @groups; 
    } 

La salida muestra todos los grupos. Las coincidencias más externas aparecen primero y las coincidencias anidadas para más adelante:

Found: 
    <brackets in <nested brackets> > 
    <another group <nested once <nested twice> > > 

Found: 
    <nested brackets> 

Found: 
    <nested once <nested twice> > 

Found: 
    <nested twice> 
Cuestiones relacionadas