Nie-chciwe wyrażenia regularne w Pythonie

150

Jak utworzyć takie wyrażenie regularne w Pythonie "(.*)", mając dane "a (b) c (d) e"dopasowania w Pythonie "b"zamiast "b) c (d"?

Wiem, że mogę użyć "[^)]"zamiast ".", ale szukam bardziej ogólnego rozwiązania, które utrzyma moje wyrażenie regularne nieco bardziej przejrzystym. Czy jest jakiś sposób, aby powiedzieć Pythonowi „hej, dopasuj to tak szybko, jak to możliwe”?

So8res
źródło

Odpowiedzi:

209

Szukasz wszechmocnego *?

Z dokumentów, Greedy versus Non-Greedy

nie-zachłanne kwalifikacyjne *?, +?, ??, lub {m,n}?[...] mecz jako mało tekstu, jak to możliwe.

Trey Stout
źródło
Według Internet Archive, wszystko, na co wskazywał odsyłacz, to kopia dokumentacji modułu „re” w Pythonie, więc link Treya działa równie dobrze.
spiffytech
2
jaka jest popularna angielska nazwa tego *??
Trevor Boyd Smith
Znaki wieloznaczne @Trevor Boyd Smith
Serge
3
Nazywa się to kwalifikacją „non chciwy”
brunetton
65
>>> x = "a (b) c (d) e"
>>> re.search(r"\(.*\)", x).group()
'(b) c (d)'
>>> re.search(r"\(.*?\)", x).group()
'(b)'

Według dokumentów :

Kwalifikatory „ *”, „ +” i „ ?” są chciwe; dopasowują jak najwięcej tekstu. Czasami takie zachowanie nie jest pożądane; jeśli RE <.*>jest dopasowany do „ <H1>title</H1>”, dopasuje cały ciąg, a nie tylko „ <H1>”. Dodanie „ ?” po kwalifikatorze sprawia, że ​​mecz przebiega w sposób niechciwy lub minimalny; dopasowanych zostanie jak najmniej znaków. Użycie .*?w poprzednim wyrażeniu spowoduje dopasowanie tylko do „ <H1>”.

Paolo Bergantino
źródło
14

Nie \\(.*?\\)zadziała? To jest niechciwa składnia.

Zitrax
źródło
5

Jak powiedzieli inni, używając? modyfikator na kwantyfikatorze * rozwiąże twój bezpośredni problem, ale bądź ostrożny, zaczynasz błądzić w obszarach, w których wyrażenia regularne przestają działać i zamiast tego potrzebujesz parsera. Na przykład napis "(foo (bar)) baz" spowoduje problemy.

Chas. Owens
źródło
5

Używanie niezbyt szczerego dopasowania to dobry początek, ale sugeruję również, abyś ponownie rozważył użycie .*- co z tym?

groups = re.search(r"\([^)]*\)", x)
ojrac
źródło
3

Czy chcesz, aby pasowało do „(b)”? Zrób tak, jak sugerowali Zitrax i Paolo. Czy chcesz, aby pasowało do „b”? Robić

>>> x = "a (b) c (d) e"
>>> re.search(r"\((.*?)\)", x).group(1)
'b'
David Berger
źródło
0

Na początek nie sugeruję używania „*” w wyrażeniach regularnych. Tak, wiem, jest to najczęściej używany separator wieloznakowy, ale mimo wszystko jest to zły pomysł. Dzieje się tak dlatego, że chociaż pasuje do dowolnej liczby powtórzeń dla tego znaku, „any” zawiera 0, co jest zwykle czymś, dla którego chcesz zgłosić błąd składni, a nie akceptować. Zamiast tego sugeruję użycie +znaku, który pasuje do każdego powtórzenia długości> 1. Co więcej, z tego, co widzę, masz do czynienia z wyrażeniami o stałej długości w nawiasach. W rezultacie prawdopodobnie możesz użyć {x, y}składni, aby dokładnie określić żądaną długość.

Jeśli jednak naprawdę potrzebujesz niechcianych powtórzeń, radzę skonsultować się z wszechmocnym ?. To, po umieszczeniu po na końcu dowolnego specyfikatora powtórzenia wyrażenia regularnego, zmusi tę część wyrażenia regularnego do znalezienia najmniejszej możliwej ilości tekstu.

Biorąc to pod uwagę, byłbym bardzo ostrożny z tym, ?ponieważ podobnie jak Sonic Screwdriver w Dr. Who, ma tendencję do robienia, jak mam to ująć, „lekko” niepożądanych rzeczy, jeśli nie są dokładnie skalibrowane. Na przykład, aby użyć przykładowych danych wejściowych, zidentyfikowałoby ((1)(zwróć uwagę na brak drugiego rparena) jako dopasowanie.

Dalekowie
źródło