2.36. Grupper och ankare#

Ett mönster kan göra mer än att säga ”den här strängen matchar” – det kan plocka isär de matchade delarna och överlämna var och en till applikationen med namn. Parenteser runt en del av ett mönster gör den till en fångstgrupp; matchobjektet exponerar sedan varje grupp som en separat delsträng.

2.36.1. Fångstgrupper#

Omslut valfri del av ett mönster med (...) för att fånga det som matchade:

>>> 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'
  • Grupp 0 är alltid hela matchningen.

  • Grupp 1, 2, … är de fångade delsträngarna, numrerade från vänster till höger efter sin öppnande parentes.

  • Att anropa match.group() med ett index efter den sista gruppen ger upphov till IndexError.

Ett vanligt mönster är ”matcha en känd struktur, fånga de variabla delarna som heltal”:

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. Icke-fångande grupper#

Parenteser grupperar också ett deluttryck så att en kvantifierare kan tillämpas på hela gruppen. Det är det enda syftet med grupperingen i r'(ab)+' – ”en eller flera av ab”. Att ab dyker upp som grupp 1 är en bieffekt.

För att gruppera utan att fånga, använd (?:...)

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

Icke-fångande grupper håller gruppnumren snygga när ett mönster använder gruppering för struktur men inte bryr sig om att plocka ut varje del.

2.36.3. Ankare#

Ankare matchar inte ett tecken – de matchar en position.

  • ^ – början av strängen.

  • $ – slutet av strängen.

Ankare är det som gör att re.match() och re.search() beter sig olika. re.match(p, s) är samma sak som re.search('^' + p, s): det tvingar mönstret att börja vid position 0. Att lägga till $ i slutet av ett mönster gör sedan att mönstret matchar hela strängen och inget annat:

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

^ och $ i MicroPython re betyder alltid början och slutet av hela strängen som skickas till re.search(). Det finns ingen re.MULTILINE-flagga som får dem att matcha vid varje inbäddad radbrytning, och $ matchar inte heller positionen före en avslutande \n – det måste vara det absoluta slutet av indata. För att få radvis beteende, dela först upp indata på radbrytningar och kör mönstret på varje rad.

2.36.4. Teckenuppsättningar#

Hakparenteser definierar en explicit uppsättning tecken. Matchningen förbrukar exakt ett tecken från uppsättningen.

  • [abc] – en av a, b, c.

  • [a-z] – ett tecken i intervallet a-z (inklusive).

  • [a-zA-Z0-9] – bokstäver eller siffror. Tre intervall kombinerade.

  • [^abc]inte en av a, b, c. ^ negerar endast när det är det första tecknet inuti hakparenteserna.

Exempel:

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

Det första anropet returnerar i praktiken None eftersom den bokstavliga texten är gemener. MicroPython re har ingen re.IGNORECASE-flagga – för att matcha skiftlägesokänsligt, skriv in båda skiftlägena i uppsättningen:

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

Klassgenvägarna (\d, \s, \w och deras negerade former) kan också användas inuti [...]: [\w-] betyder ”ordtecken eller ett bokstavligt bindestreck.”

2.36.5. Giriga kontra lata kvantifierare#

Kvantifierarna *, +, ? och {m,n} är giriga som standard – de matchar så många tecken som resten av mönstret fortfarande tillåter. Ofta är det precis vad som önskas; ibland är det inte det:

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

Den giriga .+ tog hela vägen fram till det sista >. Att lägga till ? gör kvantifieraren lat – den matchar så lite som möjligt:

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

Den lata formen stannar vid det första >. Lata kvantifierare dyker ständigt upp när man extraherar balanserade avgränsare från en sträng.

2.36.6. Bakåtreferenser i substitution#

re.sub() kan referera tillbaka till fångade grupper i ersättningssträngen via \1, \2, … Substitutionen skriver om varje matchning med hjälp av de fångade delarna:

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

Varje matchning fångar två tal, och ersättningen byter plats på dem. \g<1> är en alternativ syntax för samma sak – användbart när nästa tecken i ersättningen är en siffra (r'\g<1>0' för att lägga till en bokstavlig nolla efter grupp 1 i stället för att läsa ”grupp 10”).

2.36.7. Vad som inte är tillgängligt#

En påminnelse om vad MicroPython re inte stöder, ifall ett mönster från CPython hamnar här och överraskar dig:

  • Framåtblick (?=...) och bakåtblick (?<=...) – inte implementerade.

  • Namngivna grupper (?P<name>...) och namngivna bakåtreferenser (?P=name) – inte implementerade.

  • Flaggkonstanter som re.IGNORECASE, re.MULTILINE, re.DOTALL – respekteras inte. Bygg själv den skiftlägesokänsliga uppsättningen eller dela upp indata i förväg.

  • Metoderna match.groups(), match.span(), match.start() och match.end() är spärrade på en ROM-nivå som inget levererat OpenMV-kort aktiverar. Kod som förlitar sig på dem kör inte på kameran.

Med mönster, grupper och ankare är regex-verktygslådan på kameran tillräckligt liten för att lära sig i ett svep och tillräckligt rik för att göra allt utom kontextkänslig parsning.