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.