2.35. Základy vzorů

Regulární výraz je malý jazyk pro popis řetězců podle jejich formy, nikoli podle přesného obsahu. Použijte jej, když řetězec odpovídá tehdy a jen tehdy, pokud sleduje vzor, který umíte popsat, ale nemůžete vyjmenovat – „posloupnost číslic následovaná jednotkou“, „řádek, který začíná ERROR a končí číslem“, „kterákoli z těchto přípon souborů, v libovolném pořadí, s volitelnými předponami v“.

Po re sáhněte pouze tehdy, když nestačí běžná metoda řetězce.

Každá z nich je rychlejší, čitelnější a méně náchylná k chybám než ekvivalentní regulární výraz. Regulární výrazy používejte, když záleží na formě řetězce a přesný podřetězec není podstatný.

2.35.1. Čtyři věci, které budete používat

Modul MicroPython re zpřístupňuje čtyři věci:

  • re.compile() – převede řetězec se vzorem na zkompilovaný objekt vzoru, který lze opakovaně použít.

  • re.match() – vyzkouší vzor na začátku řetězce. Vzor je ukotven na pozici 0.

  • re.search() – vyzkouší vzor kdekoli v řetězci. Vrátí první shodu.

  • re.sub() – najde každou shodu a nahradí ji.

Pozoruhodná chybějící část oproti CPythonu: žádné re.findall, žádné re.finditer, žádné re.split na úrovni modulu (zkompilované vzory mají místo toho metodu split), žádné re.fullmatch, žádné příznakové konstanty jako re.IGNORECASE. Tam, kde byste na CPythonu sáhli po některé z nich, sestavte ekvivalent z re.search() ve smyčce.

2.35.2. První vzor

Vzor r'\d+' odpovídá jedné nebo více číslicím:

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

Pár věcí, kterých si všimnout:

  • Vzor je zapsán jako surový řetězec (raw string) (r'...'), takže zpětné lomítko v \d dorazí do re místo toho, aby bylo zpracováno jako únikový znak řetězce v Pythonu. Pro vzory regulárních výrazů vždy používejte surové řetězce.

  • re.search() při úspěchu vrátí objekt shody a při neúspěchu None. Před voláním match.group() vždy zkontrolujte výsledek.

  • m.group(0) je celý text, kterému vzor odpovídal. Skupiny 1, 2, … se objeví později, jakmile vzor obsahuje zachytávací závorky.

Stejný vzor s re.match() vrátí None, protože řetězec nezačíná číslicí:

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

2.35.3. Stavební prvky vzoru

Většina užitečných vzorů se skládá z malé sady prvků. Ty, které fungují v MicroPythonu:

Doslovné znaky – každý znak, který není speciální, odpovídá sám sobě. hello odpovídá hello.

Speciální znaky. ^ $ * + ? { } [ ] \ | ( ) mají všechny významy uvedené níže. Chcete-li některý z nich přiřadit doslovně, uniknete jej zpětným lomítkem: \. odpovídá doslovné tečce.

Znakové třídy – zkratky pro běžné znakové množiny:

  • \d – libovolná číslice 0-9

  • \D – libovolný nečíselný znak

  • \s – libovolný bílý znak (mezera, tabulátor, zalomení řádku)

  • \S – libovolný nebílý znak

  • \w – libovolný „slovní“ znak: písmena, číslice, podtržítko

  • \W – libovolný neslovní znak

  • . – libovolný znak kromě zalomení řádku

Kvantifikátory – kolikrát musí předchozí prvek odpovídat:

  • * – nula nebo více (hltavé)

  • + – jeden nebo více (hltavé)

  • ? – nula nebo jeden

  • {n} – přesně n

  • {m,n} – mezi m a n (včetně)

Kombinování: \d{3}-\d{4} odpovídá třem číslicím, pomlčce, čtyřem číslicím. \s+ odpovídá jednomu nebo více bílým znakům. hello.*world odpovídá hello, čemukoli (včetně ničeho) a poté world.

Poznámka

Hltavé znamená, že kvantifikátor spotřebuje co nejvíce vstupu, dokud zbytek vzoru stále může odpovídat. Proti hello x world y world .* ve hello.*world odpovídá nejdelšímu úseku, který na konci stále ponechá world – zachytí x world y, nikoli kratší x. Totéž platí pro + a rozsahovou formu {m,n}: stroj vezme nejdelší možnou shodu a ustoupí jen tehdy, pokud zbytek vzoru selže.

2.35.4. Náhrada

re.sub() najde každou shodu a nahradí ji řetězcem. Náhrada se může odkazovat na zachycené skupiny pomocí \1, \2, … (probráno spolu se zbytkem syntaxe skupin později). Bez skupin je re.sub prosté najdi-a-nahraď nad regulárním výrazem:

>>> 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'

Třetí argument je řetězec, se kterým se pracuje; výsledkem je nový řetězec s každou shodou nahrazenou.

2.35.5. Rozdělování – pouze nad zkompilovaným vzorem

Na úrovni modulu není žádné re.split. Pro rozdělení podle regulárního výrazu nejprve zkompilujte vzor a zavolejte jeho metodu split

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

Volitelný druhý argument omezuje počet rozdělení:

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

2.35.6. Kompilace pro opakované použití

Pokud se stejný vzor spouští mnohokrát – ve smyčce nebo v často volané funkci – zkompilujte jej jednou a zkompilovaný objekt opakovaně používejte:

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

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

Volání pattern.match() a pattern.search() nad zkompilovaným objektem je totéž jako funkce na úrovni modulu, ale přeskakuje náklady na opětovnou kompilaci při každém volání.

2.35.7. Vzory, které ničemu neodpovídají

Tři vzory zvláště nachytávají vývojáře:

  • .* odpovídá prázdnému řetězci. re.search(r'.*', s).group(0) vrátí '' při jakémkoli vstupu.

  • Vzor s neuniknutým speciálním znakem je syntaktická chyba. re.compile(r'cost: $5') vyvolá ValueError, protože $ znamená „konec řetězce“. Použijte r'cost: \$5'.

  • Tečka . neodpovídá zalomení řádku. Pro shodu přes zalomení řádků napište vzor tak, aby je explicitně zpracovával pomocí [\s\S], nebo podávejte vstup po jednom řádku.

S těmito prvky může vzor odpovídat téměř libovolnému výseku textu pevné formy. Vytažení strukturovaných dat zpět ze shody vyžaduje zachytávací skupiny.