2.35. A minták alapjai

A reguláris kifejezés egy kis nyelv, amellyel a szövegeket a pontos tartalmuk helyett a formájuk alapján írhatjuk le. Akkor használd, ha egy szöveg akkor és csak akkor illeszkedik, ha követ egy olyan mintát, amelyet le tudsz írni, de nem tudsz felsorolni – „számjegyek sorozata, amelyet egy mértékegység követ”, „egy sor, amely ERROR szóval kezdődik és egy számmal végződik”, „ezen fájlkiterjesztések bármelyike, tetszőleges sorrendben, opcionális v előtagokkal”.

A re modulhoz csak akkor nyúlj, ha egy egyszerű szöveges metódus nem elég.

Mindegyik gyorsabb, könnyebben olvasható és nehezebb elrontani, mint a megfelelő regex. A regexet akkor használd, ha a szöveg formája számít, a pontos részszöveg pedig nem.

2.35.1. A négy dolog, amit használni fogsz

A MicroPython re modulja négy dolgot tesz elérhetővé:

  • re.compile() – egy mintaszöveget újrahasználható, lefordított mintaobjektummá alakít.

  • re.match() – megpróbálja a mintát egy szöveg elején. A minta a 0. pozícióhoz van horgonyozva.

  • re.search() – megpróbálja a mintát egy szöveg bármely részén. Az első illeszkedést adja vissza.

  • re.sub() – megkeres minden illeszkedést és lecseréli.

Jelentős hiányosságok a CPython-hoz képest: nincs re.findall, nincs re.finditer, nincs modulszintű re.split (a lefordított mintáknak van helyette split metódusuk), nincs re.fullmatch, és nincsenek jelzőkonstansok, mint az re.IGNORECASE. Ahol a CPython-on ezek egyikéhez nyúlnál, ott építsd fel a megfelelőjét a re.search() ciklusban való használatával.

2.35.2. Egy első minta

Az r'\d+' minta egy vagy több számjegyre illeszkedik:

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

Néhány észrevennivaló:

  • A minta nyers szövegként (r'...') van megírva, hogy a \d visszaperjele a re modulhoz jusson el, ahelyett hogy Python szövegfeloldó szekvenciaként dolgoznák fel. Regex mintákhoz mindig használj nyers szövegeket.

  • A re.search() siker esetén egy illeszkedési objektumot, kudarc esetén None értéket ad vissza. A match.group() hívása előtt mindig ellenőrizd.

  • Az m.group(0) a teljes szöveg, amelyre a minta illeszkedett. Az 1., 2., … csoport később jelenik meg, ha a minta rögzítő zárójeleket tartalmaz.

Ugyanez a minta a re.match() függvénnyel None értéket ad vissza, mert a szöveg nem kezdődik számjeggyel:

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

2.35.3. A minta részei

A leghasznosabb minták egy kis elemkészletből épülnek fel. Azok, amelyek a MicroPythonban működnek:

Literális karakterek – minden olyan karakter, amely nem speciális, önmagára illeszkedik. A hello a hello szövegre illeszkedik.

Speciális karakterek – a . ^ $ * + ? { } [ ] \ | ( ) karaktereknek mind az alábbi jelentésük van. Ha egyiküket literálisan szeretnéd illeszteni, escape-eld egy visszaperjellel: a \. egy literális pontra illeszkedik.

Karakterosztályok – gyakori karakterhalmazok rövidítései:

  • \d – bármely 0-9 számjegy

  • \D – bármely nem számjegy

  • \s – bármely whitespace karakter (szóköz, tabulátor, sortörés)

  • \S – bármely nem whitespace karakter

  • \w – bármely „szóalkotó” karakter: betűk, számjegyek, aláhúzás

  • \W – bármely nem szóalkotó karakter

  • . – bármely karakter a sortörés kivételével

Kvantorok – hányszor kell az előző elemnek illeszkednie:

  • * – nulla vagy több (mohó)

  • + – egy vagy több (mohó)

  • ? – nulla vagy egy

  • {n} – pontosan n

  • {m,n}m és n között (a határokat is beleértve)

Kombinálás: a \d{3}-\d{4} három számjegyre, egy kötőjelre, négy számjegyre illeszkedik. A \s+ egy vagy több whitespace karakterre illeszkedik. A hello.*world a hello szövegre, bármire (akár semmire is), majd a world szövegre illeszkedik.

Megjegyzés

A mohó azt jelenti, hogy a kvantor annyit fogyaszt el a bemenetből, amennyit csak tud, miközben a minta többi része még illeszkedhet. A hello x world y world ellen a hello.*world mintában lévő .* a leghosszabb olyan szakaszra illeszkedik, amely még meghagy egy world szót a végén – az x world y szöveget rögzíti, nem pedig a rövidebb x szöveget. Ugyanez igaz a + jelre és a {m,n} tartományformára is: a motor a lehető leghosszabb illeszkedést veszi, majd csak akkor lép vissza, ha a minta többi része kudarcot vall.

2.35.4. Helyettesítés

A re.sub() megkeres minden illeszkedést, és lecseréli egy szövegre. A helyettesítés a \1, \2, … formán keresztül hivatkozhat a rögzített csoportokra (ezzel a csoportszintaxis többi részével együtt később foglalkozunk). Csoportok nélkül az re.sub egy egyszerű keresés-és-csere egy regexen:

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

A harmadik argumentum az a szöveg, amelyen dolgozni kell; az eredmény egy új szöveg, amelyben minden illeszkedés le van cserélve.

2.35.5. Felbontás – csak lefordított mintán

Modulszinten nincs re.split. Egy regex mentén való felbontáshoz először fordítsd le a mintát, és hívd meg a split metódusát:

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

Az opcionális második argumentum korlátozza a felbontások számát:

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

2.35.6. Fordítás újrahasználatra

Ha ugyanaz a minta sokszor fut le – egy cikluson belül vagy egy gyakran hívott függvényben –, fordítsd le egyszer, és használd újra a lefordított objektumot:

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

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

A pattern.match() és a pattern.search() meghívása egy lefordított objektumon ugyanaz, mint a modulszintű függvények, de minden híváskor megspórolja az újrafordítás költségét.

2.35.7. Minták, amelyek semmire sem illeszkednek

Különösen három minta szokta zavarba ejteni a fejlesztőket:

  • A .* az üres szövegre illeszkedik. Az re.search(r'.*', s).group(0) bármilyen bemenetre '' értéket ad vissza.

  • Egy nem escape-elt speciális karaktert tartalmazó minta szintaktikai hiba. Az re.compile(r'cost: $5') ValueError hibát vált ki, mert a $ jelentése „a szöveg vége”. Használd az r'cost: \$5' formát.

  • A pont . nem illeszkedik a sortörésre. Több soron átívelő illeszkedéshez írd meg a mintát úgy, hogy azokat explicit módon kezelje a [\s\S] segítségével, vagy egyszerre egy sort adj át.

Ezekkel az elemekkel egy minta szinte bármilyen rögzített formájú szövegrészletre illeszkedhet. A strukturált adatok visszanyerése az illeszkedésből rögzítő csoportokat igényel.