2012-05-11 11 views
5

Básicamente, lo que estoy tratando de hacer es buscar a través de un archivo PHP grande y reemplazar cualquier bloque de código PHP que incluya la cadena "search_term" en algún lugar del mismo con algún otro código. Es decir.haciendo coincidir las cadenas más cercanas a un término de búsqueda (perl regex)

<?php 
//some stuff 
?> 
<?php 
// some more stuff 
$str = "search_term"; 
// yes... 
?> 
<?php 
// last stuff 
?> 

debe convertirse en

<?php 
//some stuff 
?> 
HELLO 
<?php 
// last stuff 
?> 

Lo que tengo hasta ahora es

$string =~ s/<\?php(.*?)search_term(.*?)\?>/HELLO/ims; 

Esto coincide correctamente con el cierre más cercano ?>, pero comienza el partido en el primer <?php, en vez del más cercano a la cadena search_term.

¿Qué estoy haciendo mal?

Respuesta

5

En general, no me gusta usar concordancia no codiciosa, porque generalmente genera problemas como este. Perl mira tu archivo, encuentra el primer '<?php', luego comienza a buscar el resto de la expresión regular. Pasa sobre el primer '?>' y el segundo '<?php' porque coinciden con .*, luego encuentra search_term y el siguiente '?>', y listo.

Coincidencia no codiciosa significa que tiene una expresión regular que coincide con más cosas de las que realmente desea, y deja que perl decida qué partido devolver. Es mejor usar una expresión regular que coincida exactamente con lo que quiere unir.En este caso, se puede obtener lo que desea mediante el uso de ((?!\?>).)* en lugar de .*? ((?!\?>) es una afirmación de preanálisis negativo)

s/<\?php((?!\?>).)*search_term((?!\?>).)*\?>/HELLO/is; 

Si esperas varias coincidencias, es posible que desee utilizar en lugar de /isg/is.

Como alternativa, sólo dividir el archivo en bloques:

@blocks = split /(\?>)/, $string; 
while (@blocks) { 
    $block = shift @blocks; 
    $sep = shift @blocks; 
    if ($block=~/search_term/) { 
     print "HELLO"; 
    } else { 
     print $block, $sep; 
    } 
} 
+0

Gracias. Lo del bloque fue en realidad ideal en mi escenario particular – Mala

2

Solo necesita volver a colocar su primer grupo de captura en su reemplazo. Algo como esto:

s/<\?php(.*)<\?php(.*?)search_term(.*?)\?>/<\?php$1HELLO/ims 
+0

acaba de probar esto ... no se deshace de la pieza antes de 'search_term' – Mala

+0

¡hurra! lo tengo trabajando con: 's/<\? php (. *) <\? php (. *?) search_term (. *?) \?>/<\? php $ 1HELLO/ims' – Mala

+0

Ah sí, está bien editado para la posteridad – Benj

0

está usando el codiciosos juego mezquino, pero que todavía puede hacer coincidir demasiado.

Matching repetitions in perlretut lo describe bien.

A veces uso coincidencias negadas para ayudar, pero no creo que ayude. Por ejemplo:

s/^[^A]*A/A/ 

para asegurarse de que mis caracteres no coinciden.

Pero normalmente no estoy tratando de cruzar varias líneas y no estoy usando perl a menos que sea necesario.

+0

Erm ¿Dónde? '. *?' no es codicioso. – Benj

+0

cierto. Estoy equivocado, pero definitivamente hay más coincidencias de las deseadas. – Julian

1
s/(.*)<\?php.*?search_term.*?\?>/${1}HELLO/ims; 

En su expresión regular, el motor de expresiones regulares está tratando de encontrar la primera aparición de una subcadena que coincide con su objetivo expresión, y lo encuentra entre la primera y la segunda <?php?>.

Al poner (.*) en el inicio de la expresión regular, que engañar al motor de expresiones regulares en ir al final de la cadena (desde .* partidos toda la cadena), y luego dar marcha atrás a los lugares donde se puede encontrar la cadena "<?php" . De esta forma, la coincidencia resultante no incluirá más fichas <?php de las necesarias.

+0

** Si ** solo quería reemplazar un bloque de código, esta sería una solución mejor que la de @ Benj. Pero así no es como leo la pregunta. –

2
$string =~ s/<\?php(?:(?!\?>|search_term).)*search_term.*?\?>/HELLO/isg; 

(?:(?!\?>|search_term).)* coincide con un carácter a la vez, después de asegurándose de que el personaje no es el comienzo de ?> o search_term. Cuando eso deja de coincidir, si el siguiente punto de la cadena es search_term, lo consume y todo después de él hasta el próximo ?>. De lo contrario, ese intento falla y comienza de nuevo en el siguiente <?php.

El punto crucial es que, al igual que la solución de @ RobertYoung, no está permitido hacer coincidir ?> mientras busca search_term. Al no coincidir con search_term tampoco, se elimina el retroceso, lo que hace que la búsqueda sea más eficiente. Dependiendo del tamaño de la cadena de origen que puede no importar, pero tampoco perjudicará notablemente el rendimiento.

@ La solución de Benj (como se publicó actualmente) no funciona. Produce la salida deseada con la cadena de muestra que proporcionó, pero eso es solo por accidente. Sólo reemplaza el último bloque de código con search_term en él, y (como comentó @mob) ignora por completo el contenido del primer bloque de código.

Cuestiones relacionadas