2.35. Grunderna i mönster

Ett reguljärt uttryck är ett litet språk för att beskriva strängar genom form snarare än genom deras exakta innehåll. Använd det när en sträng matchar om och endast om den följer ett mönster som du kan beskriva men inte räkna upp – ”en sekvens av siffror följd av en enhet”, ”en rad som börjar med ERROR och slutar med ett tal”, ”någon av dessa filändelser, i valfri ordning, med valfria v-prefix”.

Ta till re endast när en vanlig strängmetod inte räcker.

Var och en av dessa är snabbare, lättare att läsa och svårare att göra fel med än motsvarande regex. Använd regex när strängens form spelar roll och den exakta delsträngen inte gör det.

2.35.1. De fyra saker du kommer att använda

MicroPython-modulen re exponerar fyra saker:

  • re.compile() – gör en mönstersträng till ett kompilerat mönsterobjekt som du kan återanvända.

  • re.match() – prova mönstret i början av en sträng. Mönstret är förankrat vid position 0.

  • re.search() – prova mönstret var som helst i en sträng. Returnerar den första matchningen.

  • re.sub() – hitta varje matchning och ersätt den.

Anmärkningsvärda utelämnanden jämfört med CPython: ingen re.findall, ingen re.finditer, ingen re.split på modulnivå (kompilerade mönster har i stället en split-metod), ingen re.fullmatch, inga flaggkonstanter som re.IGNORECASE. Där du skulle ta till någon av dessa i CPython, bygg motsvarigheten från re.search() i en slinga.

2.35.2. Ett första mönster

Mönstret r'\d+' matchar en eller flera siffror:

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

Några saker att lägga märke till:

  • Mönstret skrivs som en rå sträng (r'...') så att bakstrecket i \d når re i stället för att bearbetas som en Python-strängsekvens. Använd alltid råa strängar för regex-mönster.

  • re.search() returnerar ett matchobjekt vid framgång och None vid misslyckande. Kontrollera alltid innan du anropar match.group().

  • m.group(0) är den fullständiga text som mönstret matchade. Grupp 1, 2, … dyker upp senare, när mönstret innehåller fångande parenteser.

Samma mönster med re.match() returnerar None eftersom strängen inte börjar med en siffra:

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

2.35.3. Delarna i ett mönster

De flesta användbara mönster byggs upp från en liten uppsättning delar. De som fungerar i MicroPython:

Bokstavliga tecken – alla tecken som inte är speciella matchar sig själva. hello matchar hello.

Specialtecken. ^ $ * + ? { } [ ] \ | ( ) har alla betydelser nedan. För att matcha ett av dem bokstavligt, undantag det med ett bakstreck: \. matchar en bokstavlig punkt.

Teckenklasser – förkortningar för vanliga teckenuppsättningar:

  • \d – valfri siffra 0-9

  • \D – valfri icke-siffra

  • \s – valfritt blanktecken (mellanslag, tabb, radbrytning)

  • \S – valfritt icke-blanktecken

  • \w – valfritt ”ord”-tecken: bokstäver, siffror, understreck

  • \W – valfritt icke-ordtecken

  • . – valfritt tecken utom radbrytning

Kvantifierare – hur många gånger den föregående delen måste matcha:

  • * – noll eller fler (girig)

  • + – en eller fler (girig)

  • ? – noll eller en

  • {n} – exakt n

  • {m,n} – mellan m och n (inklusive)

Kombinera: \d{3}-\d{4} matchar tre siffror, ett bindestreck, fyra siffror. \s+ matchar ett eller flera blanktecken. hello.*world matchar hello, vad som helst (inklusive ingenting), sedan world.

Anteckning

Girig betyder att kvantifieraren förbrukar så mycket av indata som möjligt samtidigt som resten av mönstret fortfarande kan matcha. Mot hello x world y world matchar .* i hello.*world den längsta följd som fortfarande lämnar kvar ett world i slutet – den fångar x world y, inte det kortare x. Detsamma gäller + och intervallformen {m,n}: motorn tar den längsta matchning den kan och backar endast om resten av mönstret misslyckas.

2.35.4. Substitution

re.sub() hittar varje matchning och ersätter den med en sträng. Ersättningen kan referera till fångade grupper via \1, \2, … (behandlas tillsammans med resten av gruppsyntaxen senare). Utan grupper är re.sub en rak sök-och-ersätt på ett regex:

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

Det tredje argumentet är strängen att operera på; resultatet är en ny sträng med varje matchning ersatt.

2.35.5. Uppdelning – endast på ett kompilerat mönster

Det finns ingen re.split på modulnivå. För att dela upp på ett regex, kompilera först mönstret och anropa dess split-metod:

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

Det valfria andra argumentet begränsar antalet uppdelningar:

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

2.35.6. Kompilera för återanvändning

Om samma mönster körs många gånger – inuti en slinga eller i en frekvent anropad funktion – kompilera det en gång och återanvänd det kompilerade objektet:

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

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

Att anropa pattern.match() och pattern.search() på ett kompilerat objekt är samma sak som modulnivåfunktionerna men hoppar över omkompileringskostnaden vid varje anrop.

2.35.7. Mönster som inte matchar någonting

Tre mönster i synnerhet lurar utvecklare:

  • .* matchar den tomma strängen. re.search(r'.*', s).group(0) returnerar '' för all indata.

  • Ett mönster med ett oundantaget specialtecken är ett syntaxfel. re.compile(r'cost: $5') ger upphov till ValueError eftersom $ betyder ”slutet av strängen”. Använd r'cost: \$5'.

  • Punkten . matchar inte en radbrytning. För att matcha över radbrytningar, skriv mönstret så att det hanterar dem explicit med [\s\S] eller mata in en rad i taget.

Med dessa delar kan ett mönster matcha nästan vilken fastformad del av text som helst. Att dra ut strukturerad data ur matchningen kräver fångstgrupper.