2012-05-11 30 views
5

Zasadniczo staram się przeszukać dość duży plik PHP i zastąpić dowolny blok kodu PHP, który zawiera ciąg "search_term" gdzieś w nim z innym kodem. To znaczy.pasujące do najbliższych ciągów do wyszukiwanego terminu (perl regex)

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

powinna stać

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

co mam tak daleko jest

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

To właściwie dopasowuje najbliższy zamykanie ?>, ale zaczyna mecz w pierwszej <?php, zamiast najbliżej ciągu search_term.

Co robię źle?

Odpowiedz

5

Ogólnie rzecz biorąc, nie lubię używać nie-chciwych dopasowań, ponieważ zwykle prowadzi to do takich problemów. Perl sprawdza twój plik, znajduje pierwsze '<?php', a następnie rozpoczyna poszukiwania regexp. Przechodzi on przez pierwsze '?>' i drugie '<?php', ponieważ pasują one do .*, a następnie znajduje search_term i następne '?>' i gotowe.

Non-chciwy dopasowanie oznacza, że ​​masz regularne wyrażenie, które pasuje więcej rzeczy, niż naprawdę chcesz, i pozostawia to do perla, aby zdecydować, który mecz powrócić. Lepiej użyć wyrażenia regularnego, które pasuje dokładnie do tego, co chcesz dopasować.W tym przypadku, można dostać to, co chcesz za pomocą ((?!\?>).)* zamiast .*? ((?!\?>) jest ujemne wygląd wyprzedzeniem twierdzenie)

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

Jeśli oczekujesz wiele odpowiedników, możesz użyć /isg zamiast /is.

Alternatywnie, po prostu podzielić plik na bloki:

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

Dziękuję. W moim szczególnym scenariuszu blok był naprawdę idealny – Mala

2

Wystarczy, że położysz swoją pierwszą grupę przechwytującą z powrotem na miejsce wymiany. Coś takiego:

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

próbowałem tego ... nie pozbywa się części przed 'search_term' – Mala

+0

hooray! działa z: 's/<\? php (. *) <\? php (. *?) search_term (. *?) \?>/<\? php 1HELLO/ims' – Mala

+0

Ah tak, ok edytowane dla potomności. – Benj

0

Używasz chciwy skąpy dopasowanie ale nadal można dopasować zbyt wiele.

Matching repetitions in perlretut opisuje to dobrze.

Czasami używam zapóźnionych dopasowań, aby pomóc, ale nie sądzę, że to pomoże. Na przykład:

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

, aby upewnić się, że moje postacie nie są dopasowane.

Ale zwykle nie próbuję przekraczać wielu linii i nie używam perla, chyba że muszę.

+0

Erm gdzie? '. *?' nie jest chciwe. – Benj

+0

Prawda. Myliłem się, ale na pewno jest więcej dopasowanych, niż jest to pożądane. – Julian

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

W swojej wyrażenia regularnego, silnik regex próbuje znaleźć najwcześniejsze wystąpienie podciągu, który pasuje do Twojego wyraz docelowy, i uzna go między pierwszym a drugim <?php?>.

Stawiając (.*) na początku regex, oszukać silnik regex się dzieje na końcu łańcucha (od .* dopasowuje cały ciąg znaków), a następnie wycofywania do miejsc, gdzie można go znaleźć ciąg „<?php” . W ten sposób wynikowy mecz nie będzie zawierał więcej żetonów niż jest to konieczne.

+0

** Jeśli ** chciałbyś tylko zastąpić jeden blok kodu, byłoby to lepsze rozwiązanie niż @ Benj. Ale tak nie czytam. –

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

(?:(?!\?>|search_term).)* mecze jeden znak naraz, po upewniając się, że znak nie jest początek ?> lub search_term. Kiedy to przestanie pasować, jeśli następna rzecz w ciągu znaków jest search_term, zużywa to i wszystko po nim do następnego ?>. W przeciwnym razie próba ta nie powiedzie się i zaczyna się od następnej wartości <?php.

Najważniejsze jest to, że podobnie jak w przypadku rozwiązania @ RobertYoung, nie można dopasować ?> podczas wyszukiwania search_term. Dzięki temu, że nie pasuje on do search_term, eliminuje on cofanie, co sprawia, że ​​wyszukiwanie jest bardziej wydajne. W zależności od rozmiaru łańcucha źródłowego, który może nie mieć znaczenia, ale także nie wpłynie znacząco na wydajność.

@ Rozwiązanie Benj (obecnie opublikowane) nie działa. Daje pożądaną moc wyjściową z podanym ciągiem próbek, ale to tylko przypadek. Zastępuje tylko blok kodu ostatni z search_term w nim i (jak komentuje @mob) całkowicie ignoruje zawartość pierwszego bloku kodu.

Powiązane problemy