2012-05-11 10 views
5

In sostanza, quello che sto cercando di fare è cercare un file PHP piuttosto grande e sostituire qualsiasi blocco di codice PHP che includa la stringa "search_term" da qualche parte in esso con qualche altro codice. Cioècorrispondente alle stringhe più vicine a un termine di ricerca (espressione regolare perl)

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

dovrebbe diventare

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

Quello che ho finora è

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

Ciò corrisponde correttamente la chiusura più vicina ?>, ma inizia la partita al primissimo <?php, invece di quello più vicino alla stringa search_term.

Cosa sto sbagliando?

risposta

5

Generalmente, non mi piace usare corrispondenze non avide, perché di solito porta a problemi come questo. Perl guarda il tuo file, trova il primo '<?php', quindi inizia a cercare il resto del regexp. Passa sopra il primo '?>' e il secondo '<?php' perché corrispondono a .*, quindi trova search_term e il successivo '?>' ed è fatto.

Una corrispondenza non avara significa che si ha un'espressione regolare che corrisponde a più cose di quanto si desideri realmente e lascia a perl decidere quale corrispondenza restituire. È meglio usare un'espressione regolare che corrisponde esattamente a ciò che si desidera abbinare.In questo caso, è possibile ottenere ciò che si desidera utilizzando ((?!\?>).)* invece di .*? ((?!\?>) è un aspetto negativo asserzione look-ahead)

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

Se ci si aspetta più corrispondenze, si potrebbe desiderare di utilizzare /isg piuttosto che /is.

In alternativa, basta dividere il file in blocchi:

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

Grazie. La cosa del blocco era in realtà ideale nel mio particolare scenario – Mala

2

Hai solo bisogno di rimettere il tuo primo gruppo di cattura nella tua sostituzione. Qualcosa di simile a questo:

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

appena provato questo ... non si sbarazza della parte prima di 'search_term' – Mala

+0

evviva! funziona con: 's/<\? php (. *) <\? php (. *?) search_term (. *?) \?>/<\? php $ 1HELLO/ims' – Mala

+0

Ah sì, ok modificato per i posteri. – Benj

0

Si utilizza avido corrispondenza avaro, ma che è ancora possibile abbinare troppo.

Matching repetitions in perlretut lo descrive bene.

A volte uso le corrispondenze negate per aiutare ma non penso che possa essere d'aiuto. Ad esempio:

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

per accertarsi che i miei caratteri non corrispondano.

Ma di solito non sto cercando di attraversare più righe e non sto usando perl a meno che non sia necessario.

+0

Erm dove? '. *?' è non-goloso. – Benj

+0

Vero. Mi sbaglio, ma c'è sicuramente più abbinamenti del voluto. – Julian

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

Nella tua espressione regolare, il motore regex sta cercando di trovare la prima occorrenza di una sottostringa che corrisponde al tuo espressione bersaglio, e lo trova tra il primo e il secondo <?php?>.

Mettendo (.*) all'inizio della regex, si ingannare il motore regex ad andare alla fine della stringa (in quanto .* corrisponde l'intera stringa), per poi tornare indietro a punti in cui si può trovare la stringa "<?php" . In questo modo la corrispondenza risultante non includerà altri token <?php del necessario.

+0

** Se ** si desidera solo sostituire un blocco di codice, questa sarebbe una soluzione migliore di quella di @ Benj. Ma non è così che leggo la domanda. –

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

(?:(?!\?>|search_term).)* partite di un carattere alla volta, dopo aver assicurandosi che il personaggio non è l'inizio di ?> o search_term. Quando questo si interrompe, se la stringa successiva è search_term, consuma tutto ciò che segue fino al prossimo ?>. Altrimenti, il tentativo fallisce e ricomincia dal prossimo <?php.

Il punto cruciale è che, come la soluzione di @ RobertYoung, non è consentito corrispondere a ?> mentre cerca search_term. In caso contrario, elimina anche il backtracking, il che rende la ricerca più efficiente. A seconda delle dimensioni della stringa sorgente che potrebbe non avere importanza, ma non pregiudicherà sensibilmente le prestazioni.

@ La soluzione di Benj (come attualmente pubblicato) non funziona. Produce l'output desiderato con la stringa di esempio che hai fornito, ma è solo per caso. Sostituisce solo lo ultimo blocco con search_term in esso e (come @mob commentato) ignora completamente il contenuto del primo blocco di codice.

Problemi correlati