2.36. Skupiny a kotvy

Vzor umí víc než jen říct „tento řetězec odpovídá“ – dokáže rozebrat odpovídající části a každou z nich předat aplikaci pod jménem. Závorky kolem části vzoru z ní udělají zachytávací skupinu (capturing group); objekt shody pak každou skupinu zpřístupní jako samostatný podřetězec.

2.36.1. Zachytávací skupiny

Obalením libovolné části vzoru do (...) zachytíte to, čemu odpovídala:

>>> 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'
  • Skupina 0 je vždy celá shoda.

  • Skupiny 1, 2, … jsou zachycené podřetězce, číslované zleva doprava podle jejich otevírací závorky.

  • Volání match.group() s indexem za poslední skupinou vyvolá IndexError.

Běžným postupem je „najít známou strukturu a proměnlivé části zachytit jako celá čísla“:

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. Nezachytávací skupiny

Závorky také seskupují dílčí výraz, aby se kvantifikátor mohl vztahovat na celou skupinu. To je jediný účel seskupení v r'(ab)+' – „jeden nebo více výskytů ab“. Skutečnost, že se ab objeví jako skupina 1, je vedlejší efekt.

Pro seskupení bez zachytávání použijte (?:...)

>>> re.search(r'(?:ab)+', 'xababy').group(0)
'abab'

Nezachytávací skupiny udržují čísla skupin přehledná, když vzor používá seskupení kvůli struktuře, ale nezajímá ho vytahování jednotlivých částí.

2.36.3. Kotvy

Kotvy neodpovídají žádnému znaku – odpovídají pozici.

  • ^ – začátek řetězce.

  • $ – konec řetězce.

Kotvy způsobují, že se re.match() a re.search() chovají rozdílně. re.match(p, s) je totéž co re.search('^' + p, s): vynutí, aby vzor začínal na pozici 0. Přidání $ na konec vzoru pak způsobí, že vzor odpovídá celému řetězci a ničemu jinému:

>>> re.search(r'^\d+$', '12345')
<match num=1>
>>> re.search(r'^\d+$', '12345 ok') is None
True

^ a $ v MicroPython re vždy znamenají začátek a konec celého řetězce předaného do re.search(). Neexistuje žádný příznak re.MULTILINE, který by je nutil odpovídat na každém vnořeném zalomení řádku, a $ neodpovídá ani pozici před koncovým \n – musí to být úplný konec vstupu. Pro chování po jednotlivých řádcích nejprve rozdělte vstup podle zalomení řádků a spusťte vzor na každém řádku.

2.36.4. Znakové množiny

Hranaté závorky definují explicitní množinu znaků. Shoda spotřebuje přesně jeden znak z této množiny.

  • [abc] – jeden ze znaků a, b, c.

  • [a-z] – jeden znak v rozsahu a-z (včetně).

  • [a-zA-Z0-9] – písmena nebo číslice. Tři rozsahy spojené dohromady.

  • [^abc]žádný ze znaků a, b, c. ^ neguje pouze tehdy, je-li prvním znakem uvnitř závorek.

Příklady:

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

První volání v praxi vrátí None, protože doslovný text je psaný malými písmeny. MicroPython re nemá žádný příznak re.IGNORECASE – pro shodu bez ohledu na velikost písmen zapište do množiny obě varianty:

>>> re.search(r'[A-Fa-f0-9]{6}', 'colour #1a2b3c rest').group(0)
'1a2b3c'

Zkratky tříd (\d, \s, \w a jejich negované formy) lze používat i uvnitř [...]: [\w-] znamená „slovní znaky nebo doslovná pomlčka“.

2.36.5. Hltavé vs. líné kvantifikátory

Kvantifikátory *, +, ? a {m,n} jsou ve výchozím stavu hltavé (greedy) – odpovídají tolika znakům, kolik jen zbytek vzoru ještě dovolí. Často je to přesně to, co chceme; někdy ale ne:

>>> re.search(r'<(.+)>', 'a <b> <c> d').group(1)
'b> <c'

Hltavé .+ pohltilo vše až k poslednímu >. Připojení ? udělá z kvantifikátoru líný (lazy) – odpovídá co nejméně:

>>> re.search(r'<(.+?)>', 'a <b> <c> d').group(1)
'b'

Líná forma se zastaví u prvního >. Líné kvantifikátory přicházejí na řadu neustále při vytahování vyvážených oddělovačů z řetězce.

2.36.6. Zpětné odkazy v náhradě

re.sub() se může v náhradním řetězci odkazovat zpět na zachycené skupiny pomocí \1, \2, … Náhrada přepíše každou shodu s využitím zachycených částí:

>>> re.sub(r'(\d+)\.(\d+)', r'\2.\1', 'swap 12.34 and 5.6')
'swap 34.12 and 6.5'

Každá shoda zachytí dvě čísla a náhrada je prohodí. \g<1> je alternativní syntaxe pro totéž – užitečná, když je dalším znakem v náhradě číslice (r'\g<1>0' připojí doslovnou nulu ke skupině 1, místo aby se to četlo jako „skupina 10“).

2.36.7. Co není k dispozici

Připomenutí toho, co MicroPython re nepodporuje, pro případ, že by sem zabloudil vzor z CPythonu a překvapil vás:

  • Lookahead (?=...) a lookbehind (?<=...) – nejsou implementovány.

  • Pojmenované skupiny (?P<name>...) a pojmenované zpětné odkazy (?P=name) – nejsou implementovány.

  • Příznakové konstanty jako re.IGNORECASE, re.MULTILINE, re.DOTALL – nejsou respektovány. Sestavte si množinu necitlivou na velikost písmen nebo si vstup rozdělte sami předem.

  • Metody match.groups(), match.span(), match.start() a match.end() jsou podmíněny úrovní ROM, kterou žádná dodávaná deska OpenMV nepovoluje. Kód, který se na ně spoléhá, na kameře nepoběží.

Se vzory, skupinami a kotvami je sada nástrojů pro regulární výrazy na kameře dostatečně malá na to, aby se dala naučit za jedno posezení, a zároveň dostatečně bohatá, aby zvládla vše kromě kontextově citlivého parsování.