jaka jest różnica pomiędzy ?:, ?! i? = w wyrażeniu regularnym?


107

Szukałem znaczenia tych wyrażeń, ale nie mogłem zrozumieć dokładnej różnicy między nimi. Oto, co mówią:

  • ?: Dopasuj wyrażenie, ale go nie przechwytuj.
  • ?= Dopasuj przyrostek, ale wyklucz go z przechwytywania.
  • ?! Dopasuj, jeśli brak sufiksu.

Próbowałem używać ich w prostym wyrażeniu regularnym i otrzymałem podobne wyniki dla wszystkich. przykład: poniższe 3 wyrażenia dają bardzo podobne wyniki.

  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?!\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?=\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9]+)*

Pokaż nam swój przypadek testowy. Nie powinny dawać takich samych wyników.
Bergi

@ sepp2k, to samo podobne wyniki w kilku przypadkach, jeden z nich wspomniany w pytaniu.
RK Poddar

@Bergi, przetestowałem to z losowymi danymi, zawierającymi angielskie słowa, numery telefonów, adresy URL, adresy e-mail, numery itp.
RK Poddar

4
@RKAgarwal Ach, widzę, co tam zrobiłeś. Dodałeś *po grupach, więc są po prostu ignorowane.
sepp2k

uwaga : użyjesz ich tylko na początku nawiasów, a nawiasy tworzą grupę przechwytującą (różne zestawy nawiasów wyodrębniają różne sekcje tekstu).
Ryan Taylor

Odpowiedzi:


152

Różnica między ?=i ?!polega na tym, że to pierwsze wymaga dopasowania danego wyrażenia, a drugie wymaga, aby nie pasowało. Na przykład a(?=b)dopasuje „a” do „ab”, ale nie „a” w „ac”. Natomiast a(?!b)dopasuje „a” do „ac”, ale nie „a” in „ab”.

Różnica między ?:i ?=polega na tym, że ?=wyklucza wyrażenie z całego dopasowania, a ?:po prostu nie tworzy grupy przechwytywania. Na przykład a(?:b)dopasuje „ab” do „abc”, a a(?=b)dopasuje tylko „a” do „abc”. a(b)dopasuje „ab” w „abc” i utworzy przechwycenie zawierające „b”.


80
?:  is for non capturing group
?=  is for positive look ahead
?!  is for negative look ahead
?<= is for positive look behind
?<! is for negative look behind

Proszę sprawdzić tutaj: http://www.regular-expressions.info/lookaround.html, aby zapoznać się z bardzo dobrym samouczkiem i przykładami dotyczącymi lookahead w wyrażeniach regularnych.


15
Jednak JavaScript nie zna lookbehind.
Bergi

1
Ten jest bardziej kompletny dla ogólnego wyrażenia regularnego.
Yan Yang,

/ (? <= ^ a) b / pracował dla mnie w javascript! Wygląda na to, że w Internecie nie ma samouczka dotyczącego przeglądania języka JavaScript.
Y. Yoshii

Dopiero najnowsze wersje przeglądarek zaczęły obsługiwać przeglądanie wstecz w JS
anubhava

- anubhava Nie znam żadnej alternatywy dla / (? <= ^ A) b / używając czystego wyrażenia regularnego. Być może mogę, ale musiałbym polegać na funkcjach zwrotnych.
Y. Yoshii

21

Aby lepiej zrozumieć, zastosujmy trzy wyrażenia oraz grupę przechwytywania i przeanalizujmy każde zachowanie.

  • () grupa przechwytująca - wyrażenie regularne wewnątrz nawiasów musi być dopasowane, a dopasowanie tworzy grupę przechwytującą
  • (?:) grupa nie przechwytująca - wyrażenie regularne wewnątrz nawiasów musi być dopasowane, ale nie tworzy grupy przechwytującej
  • (?=) pozytywne spojrzenie w przyszłość - zapewnia, że ​​wyrażenie regularne musi zostać dopasowane
  • (?!) negatywne spojrzenie w przyszłość - zapewnia, że ​​dopasowanie wyrażenia regularnego nie jest możliwe

Złóżmy wniosek q(u)io rzucenie palenia . qpasuje q, a grupa przechwytująca upasuje do u . Mecz wewnątrz grupy przechwytujących zostaje podjęty i zostaje utworzona grupa przechwytująca. Więc silnik kontynuuje i. I ibędzie pasować do i . Ta ostatnia próba dopasowania zakończyła się sukcesem. qui zostanie dopasowany i zostanie utworzona grupa przechwytywania z u .

Złóżmy wniosek q(?:u)io rzucenie palenia . Ponownie, qdopasowuje q, a grupa bez przechwytywania upasuje do u . Dopasowanie z grupy bez przechwytywania jest brane, ale grupa przechwytująca nie jest tworzona. Więc silnik kontynuuje i. I ibędzie pasować do i . Ta ostatnia próba dopasowania zakończyła się sukcesem. qui jest dopasowane

Złóżmy wniosek q(?=u)io rzucenie palenia . Lookahead jest dodatni i następuje po nim kolejny token. Ponownie qdopasowuje q i udopasowuje u . Ponownie, dopasowanie z lookahead musi zostać odrzucone, więc silnik cofa się z iciągu do u . Lookahead powiodło się, więc silnik działa dalej i. Ale inie można dopasować do ciebie . Więc ta próba dopasowania kończy się niepowodzeniem.

Złóżmy wniosek q(?=u)uo rzucenie palenia . Lookahead jest dodatni i następuje po nim kolejny token. Ponownie qdopasowuje q i udopasowuje u . Dopasowanie z wyprzedzenia musi zostać odrzucone, więc silnik cofa się z uciągu do u . Lookahead powiodło się, więc silnik działa dalej u. I upasuje do ciebie . Więc ta próba dopasowania zakończyła się sukcesem. qu jest dopasowany

Złóżmy wniosek q(?!i)uo rzucenie palenia . Nawet w tym przypadku lookahead jest dodatni (ponieważ inie pasuje) i następuje po nim inny token. Ponownie qpasuje do q, a inie do u . Dopasowanie z wyprzedzenia musi zostać odrzucone, więc silnik cofa się z uciągu do u . Lookahead powiodło się, więc silnik działa dalej u. I upasuje do ciebie . Więc ta próba dopasowania zakończyła się sukcesem. qu jest dopasowany

Podsumowując, prawdziwa różnica między grupami z wyprzedzeniem i bez przechwytywania polega na tym, czy chcesz po prostu przetestować istnienie lub przetestować i zapisać dopasowanie. Przechwytywanie grupy jest drogie, więc używaj jej rozsądnie.


> więc silnik cofa się z i w ciągu do u. Lookahead powiodło się, więc silnik kontynuuje z i. Ale nie mogę się z tobą dopasować TO jest całkowicie zagmatwane. Po co się cofać, skoro to patrzenie w przód ?
Green

1
@Zielony Ważną rzeczą do zrozumienia o lookahead i innych konstrukcjach obejścia jest to, że chociaż przechodzą przez ruchy, aby sprawdzić, czy ich podwyrażenie jest w stanie się dopasować, w rzeczywistości nie „konsumują” żadnego tekstu. To może być trochę zagmatwane
freedev

7

Spróbuj dopasować foobardo tych:

/foo(?=b)(.*)/
/foo(?!b)(.*)/

Pierwsze wyrażenie regularne będzie pasowało i zwróci „bar” jako pierwsze przesłanie - (?=b)dopasowuje „b”, ale go nie zużywa, pozostawiając je dla następujących nawiasów.

Drugie wyrażenie regularne NIE będzie pasować, ponieważ oczekuje, że po „foo” nastąpi coś innego niż „b”.

(?:...)ma dokładnie taki sam efekt jak simple (...), ale nie zwraca tej części jako przesłania.


0

Najprostszym sposobem zrozumienia asercji jest traktowanie ich jako polecenia wstawionego do wyrażenia regularnego. Gdy silnik przejdzie do asercji, natychmiast sprawdzi stan opisany w asercji. Jeśli wynik jest prawdziwy, kontynuuj wykonywanie wyrażenia regularnego.


0

To jest prawdziwa różnica:

>>> re.match('a(?=b)bc', 'abc')
<Match...>
>>> re.match('a(?:b)c', 'abc')
<Match...>

# note:
>>> re.match('a(?=b)c', 'abc')
None

Jeśli nie obchodzi Cię zawartość po „?:” Lub „? =”, „?:” I „? =” Są takie same. Oba są w porządku.

Ale jeśli potrzebujesz tych treści do dalszego przetwarzania (nie tylko dopasowywania całości. W takim przypadku możesz po prostu użyć "a (b)") Zamiast tego musisz użyć "? =". Ponieważ „?:” Po prostu przez to zniknie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.