2.35. Noțiuni de bază despre tipare¶
O expresie regulată este un mic limbaj pentru descrierea șirurilor după formă, mai degrabă decât după conținutul lor exact. Folosește-o atunci când un șir corespunde dacă și numai dacă urmează un tipar pe care îl poți descrie, dar nu îl poți enumera – „o secvență de cifre urmată de o unitate”, „o linie care începe cu ERROR și se termină cu un număr”, „oricare dintre aceste extensii de fișier, în orice ordine, cu prefixe v opționale”.
Apelează la re numai atunci când o metodă simplă de șir nu este suficientă.
str.startswith(),str.endswith()– testarea unui prefix sau sufix fix.in– testarea dacă un subșir fix este prezent.str.split(),str.find(),str.replace()– lucrul cu delimitatori ficși.
Fiecare dintre acestea este mai rapidă, mai ușor de citit și mai greu de greșit decât regex-ul echivalent. Folosește regex atunci când contează forma șirului, iar subșirul exact nu contează.
2.35.1. Cele patru lucruri pe care le vei folosi¶
Modulul MicroPython re expune patru lucruri:
re.compile()– transformă un șir de tipar într-un obiect tipar compilat pe care îl poți reutiliza.re.match()– încearcă tiparul la începutul unui șir. Tiparul este ancorat la poziția 0.re.search()– încearcă tiparul oriunde într-un șir. Returnează prima potrivire.re.sub()– găsește fiecare potrivire și o înlocuiește.
Omisiuni notabile față de CPython: niciun re.findall, niciun re.finditer, niciun re.split la nivel de modul (tiparele compilate au în schimb o metodă split), niciun re.fullmatch, nicio constantă de indicator precum re.IGNORECASE. Acolo unde ai apela una dintre acestea în CPython, construiește echivalentul din re.search() într-o buclă.
2.35.2. Un prim tipar¶
Tiparul r'\d+' potrivește una sau mai multe cifre:
>>> import re
>>> m = re.search(r'\d+', 'sensor reading 42 ok')
>>> m.group(0)
'42'
Câteva lucruri de observat:
Tiparul este scris ca un șir brut (
r'...'), astfel încât bara oblică inversă din\dsă ajungă lareîn loc să fie procesată ca o secvență de escape a unui șir Python. Folosește întotdeauna șiruri brute pentru tiparele regex.re.search()returnează un obiect de potrivire în caz de succes șiNoneîn caz de eșec. Verifică întotdeauna înainte de a apelamatch.group().m.group(0)este textul complet pe care l-a potrivit tiparul. Grupurile 1, 2, … apar mai târziu, odată ce tiparul conține paranteze de captură.
Același tipar cu re.match() returnează None, deoarece șirul nu începe cu o cifră:
>>> re.match(r'\d+', 'sensor reading 42 ok') is None
True
>>> re.match(r'\d+', '42 readings')
<match num=1>
2.35.3. Componentele unui tipar¶
Cele mai utile tipare sunt construite dintr-un set mic de componente. Cele care funcționează în MicroPython:
Caractere literale – orice caracter care nu este special se potrivește pe sine. hello potrivește hello.
Caractere speciale – . ^ $ * + ? { } [ ] \ | ( ) au toate semnificațiile de mai jos. Pentru a potrivi unul dintre ele literal, escapează-l cu o bară oblică inversă: \. potrivește un punct literal.
Clase de caractere – prescurtări pentru seturi de caractere frecvente:
\d– orice cifră0-9\D– orice caracter care nu este cifră\s– orice caracter de spațiere (spațiu, tab, linie nouă)\S– orice caracter care nu este de spațiere\w– orice caracter de „cuvânt”: litere, cifre, liniuță de subliniere\W– orice caracter care nu este de cuvânt.– orice caracter, cu excepția liniei noi
Cuantificatori – de câte ori trebuie să se potrivească componenta anterioară:
*– zero sau mai multe (lacom)+– unul sau mai multe (lacom)?– zero sau unul{n}– exact n{m,n}– între m și n (inclusiv)
Combinare: \d{3}-\d{4} potrivește trei cifre, o liniuță, patru cifre. \s+ potrivește unul sau mai multe caractere de spațiere. hello.*world potrivește hello, orice (inclusiv nimic), apoi world.
Notă
Lacom înseamnă că cuantificatorul consumă cât de mult posibil din intrare, lăsând totuși restul tiparului să se potrivească. Pe hello x world y world, .* din hello.*world potrivește cea mai lungă secvență care încă lasă un world la sfârșit – capturează x world y, nu mai scurtul x. Același lucru este valabil pentru + și forma de interval {m,n}: motorul ia cea mai lungă potrivire posibilă, apoi se retrage doar dacă restul tiparului eșuează.
2.35.4. Substituție¶
re.sub() găsește fiecare potrivire și o înlocuiește cu un șir. Înlocuirea poate face referire la grupurile capturate prin \1, \2, … (tratate mai târziu, împreună cu restul sintaxei de grup). Fără grupuri, re.sub este o simplă căutare-și-înlocuire pe un 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'
Al treilea argument este șirul asupra căruia se operează; rezultatul este un șir nou cu fiecare potrivire înlocuită.
2.35.5. Împărțire – doar pe un tipar compilat¶
Nu există re.split la nivel de modul. Pentru a împărți pe baza unui regex, compilează mai întâi tiparul și apelează metoda sa split
>>> sep = re.compile(r'\s*,\s*')
>>> sep.split('a , b,c , d')
['a', 'b', 'c', 'd']
Al doilea argument opțional limitează numărul de împărțiri:
>>> sep.split('a, b, c, d', 2)
['a', 'b', 'c, d']
2.35.6. Compilarea pentru reutilizare¶
Dacă același tipar rulează de multe ori – în interiorul unei bucle sau într-o funcție intens folosită – compilează-l o singură dată și reutilizează obiectul compilat:
digit_run = re.compile(r'\d+')
def first_number(line):
m = digit_run.search(line)
return int(m.group(0)) if m else None
Apelarea metodelor pattern.match() și pattern.search() pe un obiect compilat este același lucru cu funcțiile la nivel de modul, dar evită costul recompilării la fiecare apel.
2.35.7. Tipare care nu potrivesc nimic¶
Trei tipare în special îi prind pe nepregătite pe dezvoltatori:
.*potrivește șirul gol.re.search(r'.*', s).group(0)returnează''pe orice intrare.Un tipar cu un caracter special neescapat este o eroare de sintaxă.
re.compile(r'cost: $5')ridică o eroareValueError, deoarece$înseamnă „sfârșitul șirului”. Foloseșter'cost: \$5'.Punctul
.nu potrivește o linie nouă. Pentru a potrivi peste liniile noi, scrie tiparul astfel încât să le trateze explicit cu[\s\S]sau furnizează câte o linie pe rând.
Cu aceste componente, un tipar poate potrivi aproape orice porțiune de text cu formă fixă. Extragerea înapoi a datelor structurate din potrivire necesită grupuri de captură.