2012-06-21 20 views
7

Witryna, nad którą pracowałem, nie będzie dopasowywać danych przy użyciu wzorca regex w języku PHP (preg_match), który wydaje się działać wszędzie tam, gdzie go testowałem. Ten wzór to:Czy te wzory regex są różne?

<channel.*?>(.*?)</channel> 

Jest dopasowywany do kanału RSS z tagiem kanału.

Teraz serwer Pracuję będzie produkować tylko poprawny wynik, jeśli go zmienić na:

<channel.*?>(.*)?</channel> 

Moje regex nie jest najlepszy na świecie, więc zastanawiam się, czy ktoś może mi powiedzieć, czy istnieje jakaś znacząca różnica między tymi dwoma wzorcami.

Mała uwaga: zdaję sobie sprawę, że prawdopodobnie lepiej byłoby używać SimpleXML itp., Ale to wyrażenie pochodzi z poprzedniej aplikacji i z różnych powodów nie mogę go zmienić.

Z góry dziękujemy za wszelkie spostrzeżenia.

+2

jest ' '' Twój separator PCRE? Jeśli tak, modyfikator 's' może lub nie ma znaczenia (nie jestem do końca pewien). – BoltClock

+0

Tak '' 'jest ogranicznikiem PCRE. Był używany przez tego, kto napisał kod, zwykle używają '# 'lub' ~ '. – Vunus

Odpowiedz

7

W oświadczeniu (.*) podano "wybór wynosi zero lub więcej znaków", a końcowe ? czyni dopasowanie opcjonalne. Natomiast (.*?) używa "leniwej gwiazdy" (*?), która najpierw próbuje całkowicie pominąć dopasowanie. Sprawdź numer this, aby uzyskać więcej informacji.

Aby zrozumieć różnicę między normalną (chciwą) gwiazdą a leniwą gwiazdą, spójrz na poniższy przykład w PHP i zauważ, że chciwa gwiazda najlepiej pasuje do wzorca, jaki jest podany, podczas gdy leniwy gwiazdor „daje się” tak szybko, jak to wykazał wobec wzorca mecz:

$inputs = array('axb' , 'axxxb' , 'axbxb' , 'axbxxxb'); 

// GREEDY STAR (NORMAL) 
foreach($inputs as $input) 
{ 
    preg_match('/a.*b/' , $input , $greedy); 
    $greedy_matches[] = $greedy[0]; 
} 

print "<pre>"; 
print_r($greedy_matches); 
print "</pre>"; 
/* 
Array 
(
    [0] => axb 
    [1] => axxxb 
    [2] => axbxb 
    [3] => axbxxxb 
) 
*/ 



// LAZY STAR 
foreach($inputs as $input) 
{ 
    preg_match('/a.*?b/' , $input , $lazy); 
    $lazy_matches[] = $lazy[0]; 
} 

print "<pre>"; 
print_r($lazy_matches); 
print "</pre>"; 
/* 
Array 
(
    [0] => axb 
    [1] => axxxb 
    [2] => axb 
    [3] => axb 
) 
*/ 
+0

Dziękuję. To pomogło w ogromnej ilości (dzięki wszystkim, którzy odpowiedzieli). Mam jednak małe pytanie, że kod, który pierwotnie był tam "(. *?)" Wydaje się działać dobrze wszędzie, łącznie z moim własnym serwerem, ale ten pojedynczy serwer wydaje się działać tylko zgodnie z '(. *)?'. Czy to ze względu na "leniwą gwiazdę", czy też może być coś dziwnego w tym serwerze i dopasowanie do regex? – Vunus

+0

Lazy star pochodzi z Perla, IIRC, więc może się zdarzyć, że ten konkretny serwer używa biblioteki regex, która nie ma implementowanej lazy. –

+0

Właśnie sprawdziłem i ma inną wersję perla do mojej wersji, więc to musi być najlepsze wytłumaczenie, jakie słyszałem. Dzięki. – Vunus

-1

w wyrażeniach regularnych, * oznacza 0 lub więcej razy - nie ma potrzeby, aby dodać? po tym.

EDIT: jak teraz rozumiem z uwagami, chciwy czyni różnicę. Trochę przypadek testowy:

var_dump(preg_replace('/<channel.*?>(.*?).*<\/channel>/', '$1', '<channel>asd</channel>')); 
var_dump(preg_replace('/<channel.*?>(.*)?.*<\/channel>/', '$1', '<channel>asd</channel>')); 

Wyjścia

string(0) "" 
string(3) "asd" 

Jak widać używam (.*?).* i (.*)?.*, tak, że bycie chciwym by dokonać zmian. Ale, jak to nie jest to samo, w tym przykładzie nie widzę, jak to może zrobić różnicę.

+3

'*?' Jest nierównomiernym zera lub więcej kwantyfikatora. –

2

Domyślam się, że nie chcesz, aby operator był leniwy. Leniwy operator na ogół stara się dopasować tak mało, jak to tylko możliwe, co może dać nieoczekiwane rezultaty w przypadku dużej ilości danych, które mogą być nieregularne. Umieszczając znak zapytania na końcu chciwej grupy, dodajesz opcjonalne dopasowanie do chciwej grupy, a nie czynisz grupę nie-chciwą (leniwą). Jeśli chcesz przeczytać więcej o rozróżnieniu pomiędzy chciwością a lenistwem, sprawdź to: http://www.regular-expressions.info/possessive.html.

0

Podaj przykład tekstu, z którym próbujesz się dopasować.

'<channel.*' will match anything starting with <channel 

'?>' will match a single character followed by > (so '1>', '2>', 'b>' etc) 

jeśli chcesz dopasować wszystko między wystarczy użyć wzoru

'#<channel>(.*)</channel>#' 
Powiązane problemy