2.35. Osnove uzoraka

Regularni izraz mali je jezik za opisivanje nizova prema obliku, a ne prema njihovu točnom sadržaju. Koristite ga kad niz odgovara onda i samo onda ako slijedi uzorak koji možete opisati, ali ne i nabrojati – „slijed znamenki popraćen jedinicom”, „redak koji počinje s ERROR i završava brojem”, „bilo koja od ovih datotečnih ekstenzija, bilo kojim redoslijedom, s neobaveznim v prefiksima”.

Posegnite za re samo kad obična metoda niza nije dovoljna.

Svaka od tih je brža, čitljivija i manje sklona pogreškama od ekvivalentnog regularnog izraza. Koristite regularni izraz kad je bitan oblik niza, a ne i točan podniz.

2.35.1. Četiri stvari koje ćete koristiti

MicroPython modul re izlaže četiri stvari:

  • re.compile() – pretvara niz uzorka u kompilirani objekt uzorka koji možete ponovno koristiti.

  • re.match() – pokušava uzorak na početku niza. Uzorak je usidren na poziciji 0.

  • re.search() – pokušava uzorak bilo gdje u nizu. Vraća prvo podudaranje.

  • re.sub() – pronalazi svako podudaranje i zamjenjuje ga.

Značajni izostanci u odnosu na CPython: nema re.findall, nema re.finditer, nema re.split na razini modula (kompilirani uzorci umjesto toga imaju metodu split), nema re.fullmatch, nema konstanti zastavica poput re.IGNORECASE. Tamo gdje biste na CPythonu posegnuli za nečim od toga, izgradite ekvivalent iz re.search() u petlji.

2.35.2. Prvi uzorak

Uzorak r'\d+' podudara jednu ili više znamenki:

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

Nekoliko stvari koje treba primijetiti:

  • Uzorak je napisan kao sirovi niz (r'...') tako da obrnuta kosa crta u \d dospijeva do re umjesto da se obradi kao Python escape niza. Uvijek koristite sirove nizove za regex uzorke.

  • re.search() u slučaju uspjeha vraća objekt podudaranja, a None u slučaju neuspjeha. Uvijek provjerite prije poziva match.group().

  • m.group(0) je cijeli tekst koji je uzorak podudario. Grupe 1, 2, … pojavljuju se kasnije, kad uzorak sadrži zagrade za hvatanje.

Isti uzorak s re.match() vraća None jer niz ne počinje znamenkom:

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

2.35.3. Dijelovi uzorka

Većina korisnih uzoraka izgrađena je od malog skupa dijelova. Oni koji rade u MicroPythonu:

Doslovni znakovi – svaki znak koji nije poseban podudara sam sebe. hello podudara hello.

Posebni znakovi. ^ $ * + ? { } [ ] \ | ( ) svi imaju značenja navedena niže. Da biste neki od njih podudarili doslovno, escapeajte ga obrnutom kosom crtom: \. podudara doslovnu točku.

Klase znakova – kratice za uobičajene skupove znakova:

  • \d – bilo koja znamenka 0-9

  • \D – bilo koji znak koji nije znamenka

  • \s – bilo koji razmaknici znak (razmak, tabulator, prijelom retka)

  • \S – bilo koji znak koji nije razmaknik

  • \w – bilo koji „znak riječi”: slova, znamenke, podvlaka

  • \W – bilo koji znak koji nije znak riječi

  • . – bilo koji znak osim prijeloma retka

Kvantifikatori – koliko se puta prethodni dio mora podudarati:

  • * – nula ili više (pohlepno)

  • + – jedan ili više (pohlepno)

  • ? – nula ili jedan

  • {n} – točno n

  • {m,n} – između m i n (uključivo)

Kombiniranje: \d{3}-\d{4} podudara tri znamenke, crticu, četiri znamenke. \s+ podudara jedan ili više razmaknika. hello.*world podudara hello, bilo što (uključujući ništa), zatim world.

Napomena

Pohlepno znači da kvantifikator troši koliko god ulaza može, a da ostatak uzorka i dalje može podudariti. Na hello x world y world, .* u hello.*world podudara najduži niz koji na kraju i dalje ostavlja world – hvata x world y, a ne kraći x. Isto vrijedi za + i oblik raspona {m,n}: stroj uzima najduže podudaranje koje može, pa popušta tek ako ostatak uzorka ne uspije.

2.35.4. Zamjena

re.sub() pronalazi svako podudaranje i zamjenjuje ga nizom. Zamjena se može pozvati na uhvaćene grupe putem \1, \2, … (obrađeno kasnije s ostatkom sintakse grupa). Bez grupa, re.sub je izravno pronalaženje-i-zamjena na regularnom izrazu:

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

Treći argument je niz nad kojim se djeluje; rezultat je novi niz sa svakim podudaranjem zamijenjenim.

2.35.5. Dijeljenje – samo na kompiliranom uzorku

Na razini modula nema re.split. Za dijeljenje regularnim izrazom, najprije kompilirajte uzorak i pozovite njegovu metodu split

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

Neobavezni drugi argument ograničava broj dijeljenja:

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

2.35.6. Kompiliranje za ponovnu uporabu

Ako se isti uzorak izvodi mnogo puta – unutar petlje ili u često pozivanoj funkciji – kompilirajte ga jednom i ponovno koristite kompilirani objekt:

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

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

Pozivanje pattern.match() i pattern.search() na kompiliranom objektu isto je kao funkcije na razini modula, ali preskače trošak ponovnog kompiliranja pri svakom pozivu.

2.35.7. Uzorci koji ništa ne podudaraju

Tri uzorka osobito zbunjuju programere:

  • .* podudara prazan niz. re.search(r'.*', s).group(0) vraća '' za bilo koji ulaz.

  • Uzorak s neescapeanim posebnim znakom sintaksna je pogreška. re.compile(r'cost: $5') baca ValueError jer $ znači „kraj niza”. Koristite r'cost: \$5'.

  • Točka . ne podudara prijelom retka. Za podudaranje preko prijeloma retka, napišite uzorak da ih izričito obrađuje pomoću [\s\S] ili dovedite jedan po jedan redak.

S ovim dijelovima uzorak može podudariti gotovo svaki fiksni isječak teksta. Izvlačenje strukturiranih podataka iz podudaranja zahtijeva grupe za hvatanje.