2.36. Ryhmät ja ankkurit#

Säännöllinen lauseke voi tehdä muutakin kuin todeta, että ”tämä merkkijono täsmää” – se voi erottaa täsmänneet osat toisistaan ja luovuttaa kunkin sovellukselle nimellä. Sulkeet kuvion osan ympärillä tekevät siitä kaappaavan ryhmän; täsmäysolio paljastaa tämän jälkeen jokaisen ryhmän erillisenä alimerkkijonona.

2.36.1. Kaappaavat ryhmät#

Ympäröi mikä tahansa kuvion osa merkeillä (...) kaapataksesi sen, mihin se täsmäsi:

>>> import re
>>> m = re.search(r'temp (\d+) at (\d+)s', 'temp 42 at 137s ok')
>>> m.group(0)
'temp 42 at 137s'
>>> m.group(1)
'42'
>>> m.group(2)
'137'
  • Ryhmä 0 on aina koko täsmäys.

  • Ryhmät 1, 2, … ovat kaapatut alimerkkijonot, numeroituina vasemmalta oikealle niiden avaavan sulkeen mukaan.

  • Metodin match.group() kutsuminen indeksillä, joka ylittää viimeisen ryhmän, nostaa virheen IndexError.

Yleinen kuvio on ”täsmää tunnettuun rakenteeseen, kaappaa muuttuvat osat kokonaislukuina”:

def parse_temp(line):
    m = re.search(r'temp (\d+) at (\d+)s', line)
    if not m:
        return None
    return int(m.group(1)), int(m.group(2))

2.36.2. Kaappaamattomat ryhmät#

Sulkeet myös ryhmittelevät alilausekkeen niin, että määrällistäjä voi koskea koko ryhmää. Tämä on ryhmittelyn ainoa tarkoitus kuviossa r'(ab)+' – ”yksi tai useampi ab”. Se, että ab esiintyy ryhmänä 1, on sivuvaikutus.

Ryhmitelläksesi ilman kaappaamista käytä muotoa (?:...)

>>> re.search(r'(?:ab)+', 'xababy').group(0)
'abab'

Kaappaamattomat ryhmät pitävät ryhmänumeroinnin siistinä, kun kuvio käyttää ryhmittelyä rakenteen vuoksi mutta ei välitä kunkin osan poimimisesta.

2.36.3. Ankkurit#

Ankkurit eivät täsmää merkkiin – ne täsmäävät sijaintiin.

  • ^ – merkkijonon alku.

  • $ – merkkijonon loppu.

Ankkurit ovat se, mikä saa funktiot re.match() ja re.search() käyttäytymään eri tavoin. re.match(p, s) on sama kuin re.search('^' + p, s): se pakottaa kuvion alkamaan sijainnista 0. Kun kuvion loppuun lisätään $, kuvio täsmää tällöin koko merkkijonoon eikä mihinkään muuhun:

>>> re.search(r'^\d+$', '12345')
<match num=1>
>>> re.search(r'^\d+$', '12345 ok') is None
True

MicroPythonin re -moduulissa ^ ja $ tarkoittavat aina sen koko merkkijonon alkua ja loppua, joka välitetään funktiolle re.search(). Ei ole olemassa re.MULTILINE -lippua, joka saisi ne täsmäämään jokaisen upotetun rivinvaihdon kohdalla, eikä $ täsmää lopettavaa \n-merkkiä edeltävään sijaintiinkaan – sen on oltava syötteen ehdoton loppu. Saadaksesi rivikohtaisen toiminnan jaa syöte ensin rivinvaihtojen kohdalta ja aja kuvio kullekin riville.

2.36.4. Merkkijoukot#

Hakasulkeet määrittelevät eksplisiittisen merkkijoukon. Täsmäys kuluttaa täsmälleen yhden merkin joukosta.

  • [abc] – yksi merkeistä a, b, c.

  • [a-z] – yksi merkki väliltä a-z (ääripäät mukaan luettuina).

  • [a-zA-Z0-9] – kirjaimia tai numeroita. Kolme väliä yhdistettynä.

  • [^abc]ei mikään merkeistä a, b, c. Merkki ^ kieltää vain silloin, kun se on hakasulkeiden ensimmäinen merkki.

Esimerkkejä:

>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest').group(0)
'1A2B3C'
>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest') is None
True

Ensimmäinen kutsu palauttaa käytännössä None, koska literaaliteksti on pienaakkosin. MicroPythonin re -moduulissa ei ole re.IGNORECASE -lippua – täsmätäksesi kirjainkoosta riippumatta kirjoita molemmat kirjainkoot joukkoon:

>>> re.search(r'[A-Fa-f0-9]{6}', 'colour #1a2b3c rest').group(0)
'1a2b3c'

Luokkien lyhennyksiä (\d, \s, \w ja niiden kielletyt muodot) voi käyttää myös merkinnän [...] sisällä: [\w-] tarkoittaa ”sanamerkkejä tai literaaliviivaa”.

2.36.5. Ahneet ja laiskat määrällistäjät#

Määrällistäjät *, +, ? ja {m,n} ovat oletuksena ahneita – ne täsmäävät niin moneen merkkiin kuin loput kuviosta vielä sallivat. Usein juuri sitä halutaan; toisinaan ei:

>>> re.search(r'<(.+)>', 'a <b> <c> d').group(1)
'b> <c'

Ahne .+ nappasi mukaansa kaiken aina viimeiseen merkkiin > saakka. Merkin ? lisääminen tekee määrällistäjästä laiskan – se täsmää niin vähään kuin mahdollista:

>>> re.search(r'<(.+?)>', 'a <b> <c> d').group(1)
'b'

Laiska muoto pysähtyy ensimmäiseen merkkiin >. Laiskoja määrällistäjiä tarvitaan jatkuvasti, kun merkkijonosta poimitaan tasapainotettuja erottimia.

2.36.6. Takaisinviittaukset korvauksessa#

Funktio re.sub() voi viitata takaisin kaapattuihin ryhmiin korvausmerkkijonossa merkinnöillä \1, \2, … Korvaus kirjoittaa jokaisen täsmäyksen uudelleen kaapattujen osien avulla:

>>> re.sub(r'(\d+)\.(\d+)', r'\2.\1', 'swap 12.34 and 5.6')
'swap 34.12 and 6.5'

Jokainen täsmäys kaappaa kaksi numeroa, ja korvaus vaihtaa niiden paikkaa. \g<1> on vaihtoehtoinen syntaksi samalle asialle – hyödyllinen, kun korvauksen seuraava merkki on numero (r'\g<1>0' lisää literaalinollan ryhmän 1 perään sen sijaan, että lukisi ”ryhmä 10”).

2.36.7. Mitä ei ole käytettävissä#

Muistutus siitä, mitä MicroPythonin re ei tue, siltä varalta että CPythonista peräisin oleva kuvio päätyy tänne ja yllättää sinut:

  • Eteenpäinkatselu (?=...) ja taaksepäinkatselu (?<=...) – ei toteutettu.

  • Nimetyt ryhmät (?P<name>...) ja nimetyt takaisinviittaukset (?P=name) – ei toteutettu.

  • Lippuvakiot kuten re.IGNORECASE, re.MULTILINE, re.DOTALL – ei huomioida. Rakenna kirjainkoosta riippumaton joukko tai esijaa syöte itse.

  • Metodit match.groups(), match.span(), match.start() ja match.end() on portitettu ROM-tasolle, jota mikään toimitettu OpenMV-kortti ei ota käyttöön. Niihin nojaava koodi ei toimi kamerassa.

Kuvioiden, ryhmien ja ankkureiden ansiosta kameran regex-työkalupakki on tarpeeksi pieni opittavaksi yhdeltä istumalta ja tarpeeksi rikas tekemään kaiken kontekstiriippuvaista jäsennystä lukuun ottamatta.