Wyrażenia regularne są potężnym językiem do dopasowywania wzorców tekstowych. Ta strona daje podstawowe wprowadzenie do wyrażeń regularnych, wystarczające do naszych ćwiczeń w Pythonie i pokazuje, jak wyrażenia regularne działają w Pythonie. Moduł „re” Pythona zapewnia obsługę wyrażeń regularnych.

W Pythonie wyrażenie regularne jest zwykle zapisywane jako:

 match = re.search(pat, str)

Metoda re.search() pobiera wzorzec wyrażenia regularnego oraz łańcuch i szuka tego wzorca w łańcuchu. Jeśli wyszukiwanie zakończy się sukcesem, search() zwraca obiekt match lub None w przeciwnym wypadku. Dlatego, po wyszukiwaniu zwykle następuje bezpośrednio instrukcja if, aby sprawdzić, czy wyszukiwanie się powiodło, jak pokazano w poniższym przykładzie, który szuka wzorca 'słowo:’, po którym następuje trzyliterowe słowo (szczegóły poniżej):

str = 'an example word:cat!!'match = re.search(r'word:\w\w\w', str)# If-statement after search() tests if it succeededif match: print 'found', match.group() ## 'found word:cat'else: print 'did not find'

Kod match = re.search(pat, str) przechowuje wynik wyszukiwania w zmiennej o nazwie „match”. Następnie instrukcja if testuje dopasowanie — jeśli true to wyszukiwanie się powiodło i match.group() jest pasującym tekstem (np. 'word:cat’). W przeciwnym razie, jeśli dopasowanie jest fałszywe (None, aby być bardziej szczegółowym), to wyszukiwanie nie powiodło się i nie ma pasującego tekstu.

'r’ na początku łańcucha wzorca oznacza pythonowy „surowy” łańcuch, który przechodzi przez backslashe bez zmian, co jest bardzo przydatne dla wyrażeń regularnych (Java bardzo potrzebuje tej cechy!). Zalecam, abyś zawsze pisał łańcuchy wzorców z 'r’ jako nawyk.

Podstawowe wzorce

Siła wyrażeń regularnych polega na tym, że mogą one określać wzorce, a nie tylko stałe znaki. Oto najbardziej podstawowe wzorce, które pasują do pojedynczych znaków:

  • a, X, 9, < — zwykłe znaki po prostu pasują do siebie dokładnie. Metaznaki, które nie pasują do siebie, ponieważ mają specjalne znaczenie, to: . ^ $ * + ? { ( ) (szczegóły poniżej)
  • . (kropka) — pasuje do każdego pojedynczego znaku z wyjątkiem nowej linii '\n’
  • \w — (mała litera w) pasuje do znaku „słowo”: litera, cyfra lub podkreślenie . Zauważ, że chociaż „słowo” jest mnemonikiem dla tego, to dopasowuje ono tylko pojedynczy znak słowa, a nie całe słowo. \W (duża litera W) pasuje do każdego znaku niebędącego słowem.
  • \b — granica między słowem i niesłowem
  • \s — (małe litery s) dopasowuje pojedynczy znak białej przestrzeni — spację, nową linię, powrót, tabulator, formę . \S (wielka litera S) dopasowuje dowolny znak spoza białej przestrzeni.
  • \t, \n, \r — tabulator, nowa linia, powrót
  • \d — cyfra dziesiętna (niektóre starsze narzędzia regex nie obsługują tylko \d, ale wszystkie obsługują \w i \s)
  • ^ = początek, $ = koniec — dopasuj początek lub koniec łańcucha
  • \d — hamuj „specjalność” znaku. Tak więc, na przykład, użyj \, aby dopasować kropkę lub \, aby dopasować ukośnik. Jeśli nie jesteś pewien, czy znak ma specjalne znaczenie, takie jak '@’, możesz umieścić przed nim ukośnik, \@, aby upewnić się, że jest on traktowany jak znak.

Podstawowe przykłady

Żart: jak nazywasz świnię z trzema oczami? piiig!

Podstawowe zasady wyszukiwania wyrażenia regularnego dla wzorca wewnątrz łańcucha są następujące:

  • Wyszukiwanie przebiega przez łańcuch od początku do końca, zatrzymując się na pierwszym znalezionym dopasowaniu
  • Cały wzorzec musi zostać dopasowany, ale nie cały łańcuch
  • Jeśli match = re.search(pat, str) się powiedzie, match nie jest None, a w szczególności match.group() jest dopasowanym tekstem
 ## Search for pattern 'iii' in string 'piiig'. ## All of the pattern must match, but it may appear anywhere. ## On success, match.group() is matched text. match = re.search(r'iii', 'piiig') # found, match.group() == "iii" match = re.search(r'igs', 'piiig') # not found, match == None ## . = any char but \n match = re.search(r'..g', 'piiig') # found, match.group() == "iig" ## \d = digit char, \w = word char match = re.search(r'\d\d\d', 'p123g') # found, match.group() == "123" match = re.search(r'\w\w\w', '@@abcd!!') # found, match.group() == "abc"

Powtórzenie

Rzeczy stają się bardziej interesujące, gdy używasz + i * do określenia powtórzeń we wzorcu

  • + — 1 lub więcej wystąpień wzorca po jego lewej stronie, np. 'i+’ = jedno lub więcej i’s
  • * — 0 lub więcej wystąpień wzorca po jego lewej stronie
  • ? — dopasuj 0 lub 1 wystąpienie wzorca po jego lewej stronie

Leftmost & Largest

Po pierwsze wyszukiwanie znajduje najbardziej na lewo dopasowanie do wzorca, a po drugie próbuje wykorzystać jak najwięcej ciągu znaków — tj. + i * idą tak daleko, jak to możliwe (+ i * są określane jako „zachłanne”).

Przykłady powtórzeń

 ## i+ = one or more i's, as many as possible. match = re.search(r'pi+', 'piiig') # found, match.group() == "piii" ## Finds the first/leftmost solution, and within it drives the + ## as far as possible (aka 'leftmost and largest'). ## In this example, note that it does not get to the second set of i's. match = re.search(r'i+', 'piigiiii') # found, match.group() == "ii" ## \s* = zero or more whitespace chars ## Here look for 3 digits, possibly separated by whitespace. match = re.search(r'\d\s*\d\s*\d', 'xx1 2 3xx') # found, match.group() == "1 2 3" match = re.search(r'\d\s*\d\s*\d', 'xx12 3xx') # found, match.group() == "12 3" match = re.search(r'\d\s*\d\s*\d', 'xx123xx') # found, match.group() == "123" ## ^ = matches the start of string, so this fails: match = re.search(r'^b\w+', 'foobar') # not found, match == None ## but without the ^ it succeeds: match = re.search(r'b\w+', 'foobar') # found, match.group() == "bar"

Przykłady e-maili

Załóżmy, że chcesz znaleźć adres e-mail wewnątrz ciągu 'xyz [email protected] purple monkey’. Użyjemy tego jako działającego przykładu, aby zademonstrować więcej funkcji wyrażeń regularnych. Oto próba użycia wzorca r’\w+@\w+’:

 str = 'purple [email protected] monkey dishwasher' match = re.search(r'\w+@\w+', str) if match: print match.group() ## 'b@google'

W tym przypadku wyszukiwanie nie obejmuje całego adresu e-mail, ponieważ \w nie pasuje do ’-’ lub ’.’ w adresie. Naprawimy to, używając poniższych funkcji wyrażeń regularnych.

Nawiasy kwadratowe

Nawiasy kwadratowe mogą być użyte do wskazania zestawu znaków, więc pasują do 'a’ lub 'b’ lub 'c’. Kody \w, \s itd. również działają wewnątrz nawiasów kwadratowych, z jednym wyjątkiem, że kropka (.) oznacza dosłowną kropkę. W przypadku problemu z emailami, nawiasy kwadratowe są łatwym sposobem na dodanie ’.’ i ’-’ do zestawu znaków, które mogą pojawić się wokół @ z wzorcem r’+@+’, aby uzyskać cały adres email:

 match = re.search(r'+@+', str) if match: print match.group() ## '[email protected]'

(Więcej funkcji nawiasów kwadratowych) Możesz także użyć myślnika do wskazania zakresu, więc pasuje do wszystkich małych liter. Aby użyć myślnika bez wskazywania zakresu, umieść myślnik na końcu, np. . Up-hat (^) na początku zestawu nawiasów kwadratowych odwraca je, więc oznacza każdy znak z wyjątkiem 'a’ lub 'b’.

Wyodrębnianie grup

Cecha „grupy” w wyrażeniu regularnym pozwala na wyodrębnienie części pasującego tekstu. Załóżmy, że dla problemu z mailami chcemy wyodrębnić osobno nazwę użytkownika i hosta. Aby to zrobić, należy dodać nawiasy ( ) wokół nazwy użytkownika i hosta we wzorcu, jak poniżej: r'(+)@(+)’. W tym przypadku, nawiasy nie zmieniają tego co będzie pasowało do wzorca, zamiast tego tworzą logiczne „grupy” wewnątrz tekstu dopasowania. Przy udanym wyszukiwaniu, match.group(1) jest tekstem dopasowania odpowiadającym pierwszemu lewemu nawiasowi, a match.group(2) jest tekstem odpowiadającym drugiemu lewemu nawiasowi. Zwykła match.group() jest nadal całym tekstem dopasowania, jak zwykle.

 str = 'purple [email protected] monkey dishwasher' match = re.search(r'(+)@(+)', str) if match: print match.group() ## '[email protected]' (the whole match) print match.group(1) ## 'alice-b' (the username, group 1) print match.group(2) ## 'google.com' (the host, group 2)

Powszechnym sposobem pracy z wyrażeniami regularnymi jest pisanie wzorca dla rzeczy, której szukasz, dodając grupy nawiasów w celu wyodrębnienia części, które chcesz.

findall

findall() jest prawdopodobnie najpotężniejszą funkcją w module re. Powyżej użyliśmy re.search() do znalezienia pierwszego dopasowania dla wzorca. findall() znajduje *wszystkie* dopasowania i zwraca je jako listę łańcuchów, z każdym łańcuchem reprezentującym jedno dopasowanie.

 ## Suppose we have a text with many email addresses str = 'purple [email protected], blah monkey [email protected] blah dishwasher' ## Here re.findall() returns a list of all the found email strings emails = re.findall(r'+@+', str) ## for email in emails: # do something with each found email string print email

findall Z plikami

W przypadku plików, możesz mieć zwyczaj pisania pętli iterującej po liniach pliku, i mógłbyś wtedy wywołać findall() na każdej linii. Zamiast tego, pozwól findall() wykonać iterację za ciebie — znacznie lepiej! Po prostu podaj cały tekst pliku do findall() i pozwól jej zwrócić listę wszystkich dopasowań w jednym kroku (przypomnij sobie, że f.read() zwraca cały tekst pliku w pojedynczym łańcuchu):

 # Open file f = open('test.txt', 'r') # Feed the file text into findall(); it returns a list of all the found strings strings = re.findall(r'some pattern', f.read())

findall i grupy

Mechanizm grup nawiasów ( ) może być połączony z findall(). Jeśli wzorzec zawiera 2 lub więcej grup nawiasów, to zamiast zwracać listę łańcuchów, findall() zwraca listę *tupli*. Każda krotka reprezentuje jedno dopasowanie wzorca, a wewnątrz krotki znajduje się grupa(1), grupa(2) … dane. Jeśli więc do wzorca email zostaną dodane 2 grupy nawiasów, to findall() zwróci listę krotek, każda o długości 2 zawierająca nazwę użytkownika i hosta, np. (’alice’, 'google.com’).

 str = 'purple [email protected], blah monkey [email protected] blah dishwasher' tuples = re.findall(r'(+)@(+)', str) print tuples ## for tuple in tuples: print tuple ## username print tuple ## host

Gdy już masz listę krotek, możesz ją zapętlić, aby wykonać pewne obliczenia dla każdej krotki. Jeśli wzorzec nie zawiera nawiasów, to findall() zwraca listę znalezionych łańcuchów, tak jak we wcześniejszych przykładach. Jeśli wzorzec zawiera pojedynczy zestaw nawiasów, to findall() zwraca listę łańcuchów odpowiadających tej pojedynczej grupie. (Niejasna opcjonalna cecha: Czasami masz grupy paren ( ) we wzorcu, ale których nie chcesz wyodrębniać. W takim przypadku, napisz parens z ?: na początku, np. (?: ) i ten lewy paren nie będzie liczony jako wynik grupy.)

RE Workflow and Debug

Wzorce wyrażeń regularnych pakują dużo znaczenia w zaledwie kilka znaków, ale są tak gęste, że możesz spędzić dużo czasu na debugowaniu swoich wzorców. Skonfiguruj swój runtime tak, abyś mógł łatwo uruchomić wzorzec i wydrukować co pasuje, na przykład przez uruchomienie go na małym tekście testowym i wydrukowanie wyniku findall(). Jeśli wzorzec nie pasuje do niczego, spróbuj osłabić wzorzec, usuwając jego części, aby uzyskać zbyt wiele dopasowań. Gdy nie pasuje do niczego, nie możesz zrobić żadnego postępu, ponieważ nie ma nic konkretnego do oglądania. Gdy dopasowuje zbyt wiele, wtedy możesz pracować nad zaostrzaniem go przyrostowo, aby trafić dokładnie w to, co chcesz.

Opcje

Funkcje re pobierają opcje, aby zmodyfikować zachowanie dopasowania wzorca. Flaga opcji jest dodawana jako dodatkowy argument do funkcji search() lub findall() itp., np. re.search(pat, str, re.IGNORECASE).

  • IGNORECASE — ignoruje różnice wielkich/małych liter przy dopasowywaniu, tak więc 'a’ pasuje zarówno do 'a’ jak i 'A’.
  • DOTALL — zezwala kropce (.) na dopasowanie nowej linii — normalnie dopasowuje wszystko oprócz nowej linii. To może cię wyprowadzić z błędu — myślisz, że .* pasuje do wszystkiego, ale domyślnie nie wychodzi poza koniec linii. Zauważ, że \s (whitespace) obejmuje nowe linie, więc jeśli chcesz dopasować ciąg białych linii, który może zawierać nową linię, możesz po prostu użyć \s*
  • MULTILINE — W łańcuchu złożonym z wielu linii, zezwalaj ^ i $ na dopasowywanie początku i końca każdej linii. Normalnie ^/$ pasowałyby tylko do początku i końca całego łańcucha.

Greedy vs. Non-Greedy (opcjonalnie)

To jest opcjonalna sekcja, która pokazuje bardziej zaawansowaną technikę wyrażeń regularnych, która nie jest potrzebna do ćwiczeń.

Załóżmy, że masz tekst ze znacznikami: <b>foo</b> i <i>tak dalej</i>

Załóżmy, że próbujesz dopasować każdy znacznik do wzorca '(<.*>)’ — co dopasowuje najpierw?

Wynik jest trochę zaskakujący, ale chciwy aspekt .* powoduje, że dopasowuje on całe '<b>foo</b> i <i>tak dalej</i>’ jako jedno wielkie dopasowanie. Problem polega na tym, że .* idzie tak daleko jak tylko może, zamiast zatrzymać się na pierwszym > (aka jest to „chciwe”).

Istnieje rozszerzenie wyrażenia regularnego, gdzie dodajesz ? na końcu, takie jak .*? lub .+?, zmieniając je na nie-chciwe. Teraz zatrzymują się tak szybko, jak tylko mogą. Tak więc wzór '(<.*?>)’ otrzyma tylko '<b>’ jako pierwsze dopasowanie, i '</b>’ jako drugie dopasowanie, i tak dalej otrzymując każdą parę <..> po kolei. Styl jest zazwyczaj taki, że używasz .*?, a następnie bezpośrednio po jego prawej stronie szukasz jakiegoś konkretnego znacznika (> w tym przypadku), który wymusi koniec działania .*?

Rozszerzenie *? pochodzi z Perla, a wyrażenia regularne, które zawierają rozszerzenia Perla są znane jako Perl Compatible Regular Expressions — pcre. Python zawiera wsparcie dla pcre. Wiele narzędzi wiersza poleceń itp. ma flagę, w której akceptuje wzorce pcre.

Starsza, ale szeroko stosowana technika kodowania tej idei „wszystkich tych znaków z wyjątkiem zatrzymania się na X” używa stylu nawiasów kwadratowych. Dla powyższego mógłbyś napisać wzorzec, ale zamiast .* by uzyskać wszystkie znaki, użyj *, który pomija wszystkie znaki, które nie są > (wiodące ^ „odwraca” zestaw nawiasów kwadratowych, więc pasuje do każdego znaku nie znajdującego się w nawiasach).

Zastępowanie (opcjonalne)

Funkcja re.sub(pat, zamiana, str) wyszukuje wszystkie przypadki wzorca w podanym łańcuchu i zastępuje je. Łańcuch zastępujący może zawierać '\1′, '\2′, które odnoszą się do tekstu z grupy(1), grupy(2), i tak dalej od oryginalnego pasującego tekstu.

Oto przykład, który wyszukuje wszystkie adresy e-mail i zmienia je tak, aby zachować użytkownika (\1), ale mieć yo-yo-dyne.com jako hosta.

 str = 'purple [email protected], blah monkey [email protected] blah dishwasher' ## re.sub(pat, replacement, str) -- returns new string with all replacements, ##  is group(1),  group(2) in the replacement print re.sub(r'(+)@(+)', r'@yo-yo-dyne.com', str) ## purple [email protected], blah monkey [email protected] blah dishwasher

Ćwiczenia

Aby przećwiczyć wyrażenia regularne, zobacz ćwiczenie Baby Names.

admin

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.

lg