2.36. Grupy i kotwice¶
Wzorzec może zrobić więcej niż tylko stwierdzić „ten łańcuch pasuje” – potrafi rozłożyć dopasowane fragmenty i przekazać każdy z nich aplikacji według nazwy. Nawiasy wokół części wzorca tworzą grupę przechwytującą; obiekt dopasowania udostępnia wtedy każdą grupę jako osobny podłańcuch.
2.36.1. Grupy przechwytujące¶
Umieść dowolną część wzorca w (...), aby przechwycić to, co dopasowała:
>>> import re
>>> m = re.search(r'temp (\d+) at (\d+)s', 'temp 42 at 137s ok')
>>> m.group(0)
'temp 42 at 137s'
>>> m.group(1)
'42'
>>> m.group(2)
'137'
Grupa 0 to zawsze całe dopasowanie.
Grupy 1, 2, … to przechwycone podłańcuchy, numerowane od lewej do prawej według otwierającego je nawiasu.
Wywołanie
match.group()z indeksem większym niż numer ostatniej grupy zgłaszaIndexError.
Częstym wzorcem jest „dopasuj znaną strukturę, przechwyć zmienne części jako liczby całkowite”:
def parse_temp(line):
m = re.search(r'temp (\d+) at (\d+)s', line)
if not m:
return None
return int(m.group(1)), int(m.group(2))
2.36.2. Grupy nieprzechwytujące¶
Nawiasy grupują także podwyrażenie, dzięki czemu kwantyfikator może zostać zastosowany do całej grupy. To jedyny cel grupowania w r'(ab)+' – „jedno lub więcej wystąpień ab„. Fakt, że ab pojawia się jako grupa 1, jest efektem ubocznym.
Aby zgrupować bez przechwytywania, użyj (?:...)
>>> re.search(r'(?:ab)+', 'xababy').group(0)
'abab'
Grupy nieprzechwytujące utrzymują porządek w numeracji grup, gdy wzorzec wykorzystuje grupowanie do struktury, ale nie zależy mu na wyciąganiu poszczególnych fragmentów.
2.36.3. Kotwice¶
Kotwice nie dopasowują znaku – dopasowują pozycję.
^– początek łańcucha.$– koniec łańcucha.
To właśnie kotwice sprawiają, że re.match() i re.search() zachowują się różnie. re.match(p, s) jest tym samym co re.search('^' + p, s): wymusza rozpoczęcie wzorca na pozycji 0. Dodanie $ na końcu wzorca sprawia, że dopasowuje on cały łańcuch i nic poza nim:
>>> re.search(r'^\d+$', '12345')
<match num=1>
>>> re.search(r'^\d+$', '12345 ok') is None
True
^ i $ w re w MicroPython zawsze oznaczają początek i koniec całego łańcucha przekazanego do re.search(). Nie ma flagi re.MULTILINE, która sprawiłaby, że dopasowują się one przy każdym osadzonym znaku nowej linii, a $ nie dopasowuje też pozycji przed końcowym \n – musi to być absolutny koniec wejścia. Aby uzyskać zachowanie wierszowe, najpierw podziel wejście na znakach nowej linii i uruchom wzorzec na każdym wierszu.
2.36.4. Zbiory znaków¶
Nawiasy kwadratowe definiują jawny zbiór znaków. Dopasowanie pochłania dokładnie jeden znak ze zbioru.
[abc]– jeden za,b,c.[a-z]– jeden znak z zakresua-z(włącznie).[a-zA-Z0-9]– litery lub cyfry. Trzy połączone zakresy.[^abc]– nie jeden za,b,c.^neguje tylko wtedy, gdy jest pierwszym znakiem wewnątrz nawiasów.
Przykłady:
>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest').group(0)
'1A2B3C'
>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest') is None
True
Pierwsze wywołanie w praktyce zwraca None, ponieważ tekst literalny jest pisany małymi literami. re w MicroPython nie ma flagi re.IGNORECASE – aby dopasowywać bez rozróżniania wielkości liter, wpisz oba warianty do zbioru:
>>> re.search(r'[A-Fa-f0-9]{6}', 'colour #1a2b3c rest').group(0)
'1a2b3c'
Skróty klas znaków (\d, \s, \w oraz ich zanegowane formy) mogą być używane także wewnątrz [...]: [\w-] oznacza „znaki wyrazowe lub literalny myślnik”.
2.36.5. Kwantyfikatory zachłanne a leniwe¶
Kwantyfikatory *, +, ? oraz {m,n} są domyślnie zachłanne – dopasowują tyle znaków, na ile pozwoli jeszcze reszta wzorca. Często jest to dokładnie to, czego oczekujemy; czasami jednak nie:
>>> re.search(r'<(.+)>', 'a <b> <c> d').group(1)
'b> <c'
Zachłanne .+ zagarnęło wszystko aż do ostatniego >. Dopisanie ? czyni kwantyfikator leniwym – dopasowuje on tak mało, jak to możliwe:
>>> re.search(r'<(.+?)>', 'a <b> <c> d').group(1)
'b'
Forma leniwa zatrzymuje się przy pierwszym >. Leniwe kwantyfikatory pojawiają się stale przy wyciąganiu zrównoważonych ograniczników z łańcucha.
2.36.6. Odwołania wsteczne w podstawianiu¶
re.sub() może odwoływać się do przechwyconych grup w łańcuchu zastępującym za pomocą \1, \2, … Podstawianie przepisuje każde dopasowanie z użyciem przechwyconych fragmentów:
>>> re.sub(r'(\d+)\.(\d+)', r'\2.\1', 'swap 12.34 and 5.6')
'swap 34.12 and 6.5'
Każde dopasowanie przechwytuje dwie liczby, a zamiana je przestawia. \g<1> to alternatywna składnia tego samego – przydatna, gdy następny znak w zastąpieniu jest cyfrą (r'\g<1>0' dopisuje literalne zero do grupy 1, zamiast odczytywać „grupę 10”).
2.36.7. Czego nie ma¶
Przypomnienie tego, czego re w MicroPython nie obsługuje, na wypadek gdyby wylądował tu wzorzec z CPython i Cię zaskoczył:
Wyszukiwanie w przód
(?=...)i wstecz(?<=...)– niezaimplementowane.Grupy nazwane
(?P<name>...)i nazwane odwołania wsteczne(?P=name)– niezaimplementowane.Stałe flag, takie jak
re.IGNORECASE,re.MULTILINE,re.DOTALL– nieuwzględniane. Zbuduj samodzielnie zbiór niezależny od wielkości liter lub wstępnie podziel wejście.Metody
match.groups(),match.span(),match.start()imatch.end()są dostępne dopiero na poziomie ROM, którego nie włącza żadna dostarczana płytka OpenMV. Kod, który na nich polega, nie uruchomi się na kamerze.
Dzięki wzorcom, grupom i kotwicom zestaw narzędzi wyrażeń regularnych na kamerze jest na tyle mały, by nauczyć się go za jednym posiedzeniem, i na tyle bogaty, by zrobić wszystko z wyjątkiem parsowania zależnego od kontekstu.