2.35. Podstawy wzorców

Wyrażenie regularne to mały język służący do opisywania łańcuchów według ich formy, a nie dokładnej zawartości. Użyj go, gdy łańcuch pasuje wtedy i tylko wtedy, gdy spełnia wzorzec, który potrafisz opisać, ale nie potrafisz wyliczyć – „ciąg cyfr, po którym następuje jednostka”, „wiersz zaczynający się od ERROR i kończący liczbą”, „dowolne z tych rozszerzeń plików, w dowolnej kolejności, z opcjonalnymi przedrostkami v„.

Sięgaj po re tylko wtedy, gdy zwykła metoda łańcucha nie wystarczy.

Każda z nich jest szybsza, łatwiejsza do odczytania i trudniejsza do popsucia niż odpowiadające jej wyrażenie regularne. Używaj wyrażeń regularnych, gdy liczy się forma łańcucha, a dokładny podłańcuch nie.

2.35.1. Cztery rzeczy, których będziesz używać

Moduł re w MicroPython udostępnia cztery rzeczy:

  • re.compile() – zamienia łańcuch wzorca w skompilowany obiekt wzorca, którego można używać wielokrotnie.

  • re.match() – próbuje dopasować wzorzec na początku łańcucha. Wzorzec jest zakotwiczony na pozycji 0.

  • re.search() – próbuje dopasować wzorzec w dowolnym miejscu łańcucha. Zwraca pierwsze dopasowanie.

  • re.sub() – znajduje każde dopasowanie i je zastępuje.

Istotne braki w porównaniu z CPython: brak re.findall, brak re.finditer, brak re.split na poziomie modułu (skompilowane wzorce mają zamiast tego metodę split), brak re.fullmatch, brak stałych flag, takich jak re.IGNORECASE. Tam, gdzie w CPython sięgnąłbyś po jedną z nich, zbuduj odpowiednik z re.search() w pętli.

2.35.2. Pierwszy wzorzec

Wzorzec r'\d+' dopasowuje jedną lub więcej cyfr:

>>> import re
>>> m = re.search(r'\d+', 'sensor reading 42 ok')
>>> m.group(0)
'42'

Kilka rzeczy, na które warto zwrócić uwagę:

  • Wzorzec zapisano jako surowy łańcuch (r'...'), aby ukośnik wsteczny w \d trafił do re, zamiast być przetwarzanym jako sekwencja ucieczki łańcucha Python. Zawsze używaj surowych łańcuchów dla wzorców wyrażeń regularnych.

  • re.search() w razie powodzenia zwraca obiekt dopasowania, a w razie niepowodzenia None. Zawsze sprawdzaj wynik przed wywołaniem match.group().

  • m.group(0) to pełny tekst dopasowany przez wzorzec. Grupa 1, 2, … pojawiają się później, gdy wzorzec zawiera nawiasy przechwytujące.

Ten sam wzorzec z re.match() zwraca None, ponieważ łańcuch nie zaczyna się od cyfry:

>>> re.match(r'\d+', 'sensor reading 42 ok') is None
True
>>> re.match(r'\d+', '42 readings')
<match num=1>

2.35.3. Elementy wzorca

Większość użytecznych wzorców jest zbudowana z niewielkiego zbioru elementów. Te, które działają w MicroPython:

Znaki literalne – każdy znak, który nie jest specjalny, dopasowuje sam siebie. hello dopasowuje hello.

Znaki specjalne. ^ $ * + ? { } [ ] \ | ( ) mają znaczenia opisane poniżej. Aby dopasować jeden z nich literalnie, poprzedź go ukośnikiem wstecznym: \. dopasowuje literalną kropkę.

Klasy znaków – skróty dla typowych zbiorów znaków:

  • \d – dowolna cyfra 0-9

  • \D – dowolny znak niebędący cyfrą

  • \s – dowolny znak białej spacji (spacja, tabulator, znak nowej linii)

  • \S – dowolny znak niebędący białą spacją

  • \w – dowolny znak „wyrazowy”: litery, cyfry, podkreślenie

  • \W – dowolny znak niewyrazowy

  • . – dowolny znak z wyjątkiem znaku nowej linii

Kwantyfikatory – ile razy poprzedni element musi się dopasować:

  • * – zero lub więcej (zachłanny)

  • + – jedno lub więcej (zachłanny)

  • ? – zero lub jeden

  • {n} – dokładnie n

  • {m,n} – pomiędzy m a n (włącznie)

Łączenie: \d{3}-\d{4} dopasowuje trzy cyfry, myślnik, cztery cyfry. \s+ dopasowuje jeden lub więcej znaków białej spacji. hello.*world dopasowuje hello, cokolwiek (w tym nic), a następnie world.

Informacja

Zachłanny oznacza, że kwantyfikator pochłania tyle wejścia, ile zdoła, wciąż pozwalając reszcie wzorca się dopasować. W tekście hello x world y world .* we wzorcu hello.*world dopasowuje najdłuższy ciąg, który nadal pozostawia world na końcu – przechwytuje x world y, a nie krótsze x. To samo dotyczy + oraz formy zakresowej {m,n}: silnik bierze najdłuższe możliwe dopasowanie, a cofa się tylko wtedy, gdy reszta wzorca zawiedzie.

2.35.4. Podstawianie

re.sub() znajduje każde dopasowanie i zastępuje je łańcuchem. Zastąpienie może odwoływać się do przechwyconych grup za pomocą \1, \2, … (omówione później wraz z resztą składni grup). Bez grup re.sub jest zwykłym znajdź-i-zastąp na wyrażeniu regularnym:

>>> re.sub(r'\s+', ' ', 'too    many   spaces')
'too many spaces'

>>> re.sub(r'\d+', 'N', 'log 12, log 345, log 6')
'log N, log N, log N'

Trzeci argument to łańcuch, na którym ma działać operacja; wynikiem jest nowy łańcuch z zastąpionym każdym dopasowaniem.

2.35.5. Dzielenie – tylko na skompilowanym wzorcu

Nie ma re.split na poziomie modułu. Aby podzielić według wyrażenia regularnego, najpierw skompiluj wzorzec i wywołaj jego metodę split

>>> sep = re.compile(r'\s*,\s*')
>>> sep.split('a , b,c ,  d')
['a', 'b', 'c', 'd']

Opcjonalny drugi argument ogranicza liczbę podziałów:

>>> sep.split('a, b, c, d', 2)
['a', 'b', 'c, d']

2.35.6. Kompilowanie do wielokrotnego użycia

Jeśli ten sam wzorzec jest uruchamiany wiele razy – wewnątrz pętli lub w gorącej funkcji – skompiluj go raz i używaj skompilowanego obiektu wielokrotnie:

digit_run = re.compile(r'\d+')

def first_number(line):
    m = digit_run.search(line)
    return int(m.group(0)) if m else None

Wywołanie pattern.match() i pattern.search() na skompilowanym obiekcie działa tak samo jak funkcje na poziomie modułu, ale pomija koszt rekompilacji przy każdym wywołaniu.

2.35.7. Wzorce, które niczego nie dopasowują

Trzy wzorce w szczególności zaskakują programistów:

  • .* dopasowuje pusty łańcuch. re.search(r'.*', s).group(0) zwraca '' dla dowolnego wejścia.

  • Wzorzec z niepoprzedzonym ucieczką znakiem specjalnym jest błędem składni. re.compile(r'cost: $5') zgłasza ValueError, ponieważ $ oznacza „koniec łańcucha”. Użyj r'cost: \$5'.

  • Kropka . nie dopasowuje znaku nowej linii. Aby dopasowywać przez znaki nowej linii, napisz wzorzec obsługujący je jawnie za pomocą [\s\S] lub podawaj po jednym wierszu naraz.

Dzięki tym elementom wzorzec może dopasować niemal dowolny fragment tekstu o ustalonej formie. Wyciągnięcie z dopasowania strukturalnych danych wymaga grup przechwytujących.