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.
str.startswith(),str.endswith()– testa för ett fast prefix eller suffix.in– testa om en fast delsträng finns med.str.split(),str.find(),str.replace()– arbeta med fasta avgränsare.
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\dnårrei 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 ochNonevid misslyckande. Kontrollera alltid innan du anroparmatch.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 siffra0-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 tillValueErroreftersom$betyder ”slutet av strängen”. Användr'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.