Menu

Regex w Pythonie: moduł re, wzorce, grupy i zamiana

Praktyczne wprowadzenie do modułu re w Pythonie: wyszukiwanie, dopasowywanie, grupy przechwytujące, zamiana i wzorce, po które sięgasz najczęściej.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Kiedy sięgać po regex

Wyrażenia regularne to mały język do opisywania wzorców tekstu. Są potężne i łatwo ich nadużywać.

Zanim sięgniesz po re, zastanów się, czy nie wystarczą zwykłe metody napisów. .split(), .replace(), .startswith(), "target" in text są szybsze, czytelniejsze i mniej podatne na błędy niż odpowiedni regex. Regex zostaw na sytuacje, gdy wzorzec, na którym ci zależy, jest naprawdę uporządkowany, ale zbyt elastyczny dla stałych operacji na napisach: adresy e-mail, numery telefonów, linie logów o znanym kształcie, fragmenty HTML albo Markdown, każde wyszukiwanie typu "znajdź coś takiego".

Podstawowe słownictwo

Wzorzec regex to napis opisujący, czego szukasz. Kilka elementów składowych:

  • .: dowolny pojedynczy znak
  • \d: dowolna cyfra (od 0 do 9)
  • \w: dowolny znak "słowa" (litera, cyfra, podkreślnik)
  • \s: dowolny biały znak
  • ^: początek napisu
  • $: koniec napisu
  • [abc]: jeden ze znaków a, b lub c
  • [^abc]: dowolny znak oprócz a, b, c
  • a|b: a lub b
  • *: zero lub więcej wystąpień poprzedniego elementu
  • +: jedno lub więcej
  • ?: zero lub jedno
  • {3}: dokładnie 3
  • {2,5}: od 2 do 5
  • ( ... ): grupa (przechwytuje dopasowanie w środku)

To wystarczy do większości wyrażeń regularnych w praktyce.

Najważniejsze funkcje

re.search(pattern, text) znajduje pierwsze dopasowanie w dowolnym miejscu napisu. Zwraca obiekt dopasowania albo None:

Wzorce regex zawsze zapisuj jako surowe napisy (r"..."). W przeciwnym razie Python spróbuje zinterpretować ukośniki wsteczne jako sekwencje ucieczki i dostaniesz inny wzorzec niż ten, który widać w kodzie.

re.match(pattern, text) działa jak search, ale dopasowuje tylko na początku:

Zwykle chodzi ci o search.

re.findall(pattern, text) zwraca wszystkie nienachodzące na siebie dopasowania jako listę:

Zauważ, że findall zwraca napisy, a nie obiekty dopasowania. Jeśli wzorzec ma jedną grupę, dostajesz z powrotem zawartość grupy. Przy kilku grupach dostajesz listę krotek.

Grupy przechwytujące

Nawiasy we wzorcu przechwytują to, co dopasowało się w ich wnętrzu:

Grupy nazwane są zwykle czytelniejsze:

Gdy regex ma więcej niż jedną grupę, nadanie im nazw sprawia, że kod wyciągający dane jest dużo łatwiejszy do śledzenia.

Zamiana przez re.sub

re.sub(pattern, replacement, text) zamienia każde dopasowanie:

To usuwa wszystko, co nie jest cyfrą. Zamiennik może odwoływać się do przechwyconych grup przez \1, \2 itd., a w surowych napisach czytelniejsze jest \g<1>:

Jako zamiennik możesz też przekazać funkcję. Przydaje się to przy przekształceniach, które nie są prostą zamianą:

Kompilowanie wzorców do ponownego użycia

Jeśli używasz tego samego wzorca wiele razy, zwłaszcza w pętli, skompiluj go raz:

Skompilowane wzorce udostępniają te same metody (search, match, findall, sub), tylko bez wzorca jako pierwszego argumentu. Są nieco wydajniejsze i często czytelniejsze.

Flagi

Typowe modyfikatory, przekazywane jako argument flags= albo łączone operatorem OR:

  • re.IGNORECASE (re.I): dopasowanie bez rozróżniania wielkości liter.
  • re.MULTILINE (re.M): ^ i $ dopasowują się na początku i końcu każdej linii, a nie tylko na granicach napisu.
  • re.DOTALL (re.S): . dopasowuje też znaki nowej linii (domyślnie tego nie robi).
  • re.VERBOSE (re.X): pozwala na białe znaki i komentarze # we wzorcu, dla czytelności.

re.VERBOSE jest szczególnie przydatne przy złożonych wzorcach:

Wieloliniowe, skomentowane wyrażenia regularne są nieporównanie łatwiejsze w utrzymaniu niż nieprzejrzyste jednolinijkowce.

Zachłanne kontra leniwe

Kwantyfikatory (*, +, ?, {n,}) są domyślnie zachłanne: dopasowują tyle, ile się da. Dodaj ?, żeby stały się leniwe:

Wersja zachłanna przechwytuje cały fragment od <b> do </i>, co prawdopodobnie nie jest tym, o co ci chodziło. Leniwe .+? zatrzymuje się na pierwszym >.

(Na marginesie: w produkcji nie parsuj HTML wyrażeniami regularnymi. Użyj html.parser albo BeautifulSoup. Powyższy przykład służy tylko zilustrowaniu zachłanności.)

Realistyczny przykład

Parsowanie linii z prostego formatu logów:

Mnóstwo możliwości w niewielu liniach.

Kilka nawyków

  • Wzorce zawsze zapisuj jako surowe napisy.
  • Zacznij od najprostszego działającego wzorca; zawężaj później.
  • Używaj grup nazwanych, gdy masz więcej niż jedną grupę.
  • Kompiluj wzorce, których użyjesz wielokrotnie.
  • Regex jest wolniejszy od zwykłych metod napisów. Jeśli wystarczy .split(), użyj .split().

Dalej: błędy i debugowanie

To zamyka przegląd pracy z prawdziwymi danymi: pliki, JSON, CSV, HTTP, daty i regex. Jednak każdy prawdziwy program prędzej czy później trafia na błąd, a sprawne czytanie tracebacków Pythona to najważniejsza umiejętność debugowania, jaką możesz zdobyć. Ostatni rozdział omawia wyjątki i konkretne błędy, które spotkasz najczęściej.

Najczęściej zadawane pytania

Czym jest regex w Pythonie?

Wyrażenie regularne (regex) to mały język do opisywania wzorców w tekście. Moduł re w Pythonie pozwala wyszukiwać wzorce, wyciągać fragmenty i zamieniać dopasowania. Do prostych operacji na napisach to przesada (najpierw sięgnij po metody napisów, takie jak .split() czy .replace()), ale przy dopasowywaniu uporządkowanych wzorców nie ma sobie równych.

Czym różni się re.match od re.search?

re.match dopasowuje tylko na początku napisu. re.search przeszukuje cały napis i znajduje pierwsze dopasowanie w dowolnym miejscu. W razie wątpliwości użyj search: lepiej pasuje do ludzkiej intuicji.

Czy wzorce regex zawsze zapisywać jako surowe napisy?

Tak. Wzorce regex często zawierają ukośniki wsteczne (\d, \s, \b), które napisy Pythona interpretują jako sekwencje ucieczki. Przedrostek r, jak w r'\d+', mówi Pythonowi, żeby traktował napis dosłownie, dzięki czemu sam regex pozostaje czytelny.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ