2012-04-10 9 views
7

En Perl, ¿cómo escribir una expresión regular que reemplace solo hasta N coincidencias por cadena?Reemplazar solo hasta N coincidencias en una línea

I.e., estoy buscando un término medio entre s/aa/bb/; y s/aa/bb/g;. Quiero permitir múltiples sustituciones, pero solo hasta N veces.

Respuesta

4

Puedo pensar en tres maneras confiables. El primero es reemplazar todo después del N ° partido consigo mismo.

my $max = 5; 
$s =~ s/(aa)/ $max-- > 0 ? 'bb' : $1 /eg; 

Eso no es muy eficiente si hay mucho más que N coincidencias. Para eso, necesitamos mover el bucle del motor de expresiones regulares. Los siguientes dos métodos son formas de hacerlo.

my $max = 5; 
my $out = ''; 
$out .= $1 . 'bb' while $max-- && $in =~ /\G(.*?)aa/gcs; 
$out .= $1 if $in =~ /\G(.*)/gcs; 

Y esta vez, en el lugar:

my $max = 5; 
my $replace = 'bb'; 
while ($max-- && $s =~ s/\G.*?\Kaa/$replace/s) { 
    pos($s) = $-[0] + length($replace); 
} 

Usted puede verse tentado a hacer algo como

my $max = 5; 
$s =~ s/aa/bb/ for 1..$max; 

pero tal método no es efectivo para otros modelos y/o expresiones de reemplazo.

my $max = 5; 
$s =~ s/aa/ba/ for 1..$max; # XXX Turns 'aaaaaaaa' 
          #  into 'bbbbbaaa' 
          #  instead of 'babababa' 
+0

+1 para señalar el problema con' s /.../ .../para 1..N'. Pero el ejemplo tiene un defecto menor, 'aaaa' se convertiría en' bbba' no 'bbbb'. – Qtax

+0

@Qtax, Gracias.No quería rechazar a Hubert Schölnast ya que es nuevo y su respuesta realmente funciona para la pregunta específica, pero dudo que OP esté trabajando realmente con 'aa' y' bb'. Así que cubrí por qué su solución es frágil aquí. – ikegami

+0

@ikegami Ha pasado mucho tiempo desde que hice Perl, pero si pudieras hacer un seguimiento de la posición en la cadena que eres y reiniciar la búsqueda de expresiones regulares desde allí, se solucionaría el problema con 's /.../ .../para 1..N'. Aunque sería un poco feo –

1

Lo que quiere no es posible en expresiones regulares. Pero puedes poner la sustitución en un bucle for-:

my $i; 
my $aa = 'aaaaaaaaaaaaaaaaaaaa'; 
for ($i=0;$i<4;$i++) { 
    $aa =~ s/aa/bb/; 
} 
print "$aa\n"; 

resultado:

bbbbbbbbaaaaaaaaaaaa

+2

'para mi $ i (0 .. 3)' es la forma en que Perl para escribirlo. Una forma más bonita es '$ aa = ~ s/aa/bb/for 1 .. 3'. – TLP

+0

Quizás coloque un 'último a menos que $ aa = ~ ...' esté ahí para la eficacia – mob

+3

Tenga en cuenta que este enfoque puede fallar para algunos pares de expresión de búsqueda y reemplazo, p. '$ aa = ~ s/aa/ba /;' – ikegami

1

Usted puede utilizar el indicador /e que evalúa el lado derecho como expresión:

my $n = 3;  
$string =~ s/(aa)/$n-- > 0 ? "bb" : $1/ge; 
+1

Arreglaré ese error de sintaxis para ti: '$ n -> 0' (; – Qtax

1

he aquí una solución mediante el modificador/e, con el que se puede utilizar código Perl para generar la cadena de reemplazo:

 
    my $count = 0; 
    $string =~ s{ $pattern } 
       { 
       $count++; 
       if ($count < $limit) { 
        $replace; 
       } else { 
        $&; # faking a no-op, replacing with the original match. 
       } 
       }xeg; 

Con Perl 5.10 o más tarde se puede dejar caer el $ & (que tiene complicaciones de rendimiento extrañas ) y utiliza $ {^ MATCH} a través del modificador/p

 
    $string =~ s{ $pattern } 
       { 
       $count++; 
       if ($count < $limit) { 
        $replace; 
       } else { 
        ${^MATCH}; 
       } 
       }xegp; 

Es una pena que no sólo puede hacer esto, pero no se puede:

 
    last if $count >= $limit; 

Cuestiones relacionadas