2.35. Kuvioiden perusteet

Säännöllinen lauseke on pieni kieli merkkijonojen kuvaamiseen muodon eikä niiden tarkan sisällön perusteella. Käytä sitä, kun merkkijono täsmää jos ja vain jos se noudattaa kuviota, jonka osaat kuvata mutta jota et voi luetella – ”numerosarja, jota seuraa yksikkö”, ”rivi, joka alkaa merkkijonolla ERROR ja päättyy numeroon”, ”mikä tahansa näistä tiedostopäätteistä, missä tahansa järjestyksessä, valinnaisin v-etuliittein”.

Tartu re -moduuliin vain silloin, kun tavallinen merkkijonometodi ei riitä.

Kukin näistä on nopeampi, helpompi lukea ja vaikeampi tehdä väärin kuin vastaava regex. Käytä regexiä, kun merkkijonon muodolla on merkitystä eikä tarkalla alimerkkijonolla.

2.35.1. Ne neljä asiaa, joita käytät

MicroPythonin re -moduuli tuo esiin neljä asiaa:

  • re.compile() – muunna kuviomerkkijono käännetyksi kuvio-olioksi, jota voit käyttää uudelleen.

  • re.match() – kokeile kuviota merkkijonon alusta. Kuvio on ankkuroitu sijaintiin 0.

  • re.search() – kokeile kuviota missä tahansa merkkijonossa. Palauttaa ensimmäisen täsmäyksen.

  • re.sub() – etsi jokainen täsmäys ja korvaa se.

Huomattavia puutteita CPythoniin verrattuna: ei re.findall, ei re.finditer, ei moduulitason re.split (käännetyillä kuvioilla on sen sijaan split-metodi), ei re.fullmatch, ei lippuvakioita kuten re.IGNORECASE. Siellä missä CPythonissa tarttuisit johonkin näistä, rakenna vastaava funktiosta re.search() silmukassa.

2.35.2. Ensimmäinen kuvio

Kuvio r'\d+' täsmää yhteen tai useampaan numeroon:

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

Muutama huomionarvoinen asia:

  • Kuvio kirjoitetaan raakamerkkijonona (r'...'), jotta merkinnän \d kenoviiva päätyy re -moduulille sen sijaan, että se käsiteltäisiin Pythonin merkkijonopakomerkkinä. Käytä regex-kuvioissa aina raakamerkkijonoja.

  • re.search() palauttaa onnistuessaan täsmäysolion ja epäonnistuessaan None. Tarkista aina ennen metodin match.group() kutsumista.

  • m.group(0) on koko teksti, johon kuvio täsmäsi. Ryhmät 1, 2, … ilmestyvät myöhemmin, kun kuvio sisältää kaappaavia sulkeita.

Sama kuvio funktiolla re.match() palauttaa None, koska merkkijono ei ala numerolla:

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

2.35.3. Kuvion osat

Useimmat hyödylliset kuviot rakentuvat pienestä osajoukosta. Ne, jotka toimivat MicroPythonissa:

Literaalimerkit – mikä tahansa merkki, joka ei ole erikoismerkki, täsmää itseensä. hello täsmää merkkijonoon hello.

Erikoismerkit. ^ $ * + ? { } [ ] \ | ( ) ovat kaikki alla kuvattuja merkityksiä. Täsmätäksesi johonkin niistä literaalisti karkaa se kenoviivalla: \. täsmää literaalipisteeseen.

Merkkiluokat – lyhennyksiä yleisille merkkijoukoille:

  • \d – mikä tahansa numero 0-9

  • \D – mikä tahansa ei-numero

  • \s – mikä tahansa tyhjemerkki (välilyönti, sarkain, rivinvaihto)

  • \S – mikä tahansa ei-tyhjemerkki

  • \w – mikä tahansa ”sana”-merkki: kirjaimet, numerot, alaviiva

  • \W – mikä tahansa ei-sanamerkki

  • . – mikä tahansa merkki paitsi rivinvaihto

Määrällistäjät – montako kertaa edellisen osan on täsmättävä:

  • * – nolla tai useampi (ahne)

  • + – yksi tai useampi (ahne)

  • ? – nolla tai yksi

  • {n} – täsmälleen n

  • {m,n} – väliltä m ja n (ääripäät mukaan luettuina)

Yhdistäminen: \d{3}-\d{4} täsmää kolmeen numeroon, viivaan ja neljään numeroon. \s+ täsmää yhteen tai useampaan tyhjemerkkiin. hello.*world täsmää merkkijonoon hello, mihin tahansa (myös tyhjään) ja sitten merkkijonoon world.

Muista

Ahne tarkoittaa, että määrällistäjä kuluttaa syötettä niin paljon kuin pystyy ja silti antaa loppukuvion täsmätä. Syötettä hello x world y world vasten .* kuviossa hello.*world täsmää pisimpään jaksoon, joka jättää silti world-sanan loppuun – se kaappaa x world y eikä lyhyempää x. Sama pätee merkkiin + ja väliin {m,n}: moottori ottaa pisimmän täsmäyksen, jonka voi, ja perääntyy vasta jos loppukuvio epäonnistuu.

2.35.4. Korvaus

Funktio re.sub() etsii jokaisen täsmäyksen ja korvaa sen merkkijonolla. Korvaus voi viitata kaapattuihin ryhmiin merkinnöillä \1, \2, … (käsitellään myöhemmin muun ryhmäsyntaksin kanssa). Ilman ryhmiä re.sub on suora etsi-ja-korvaa regexin perusteella:

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

Kolmas argumentti on käsiteltävä merkkijono; tulos on uusi merkkijono, jossa jokainen täsmäys on korvattu.

2.35.5. Jakaminen – vain käännetyllä kuviolla

Moduulitasolla ei ole re.split-funktiota. Jakaaksesi regexin perusteella käännä kuvio ensin ja kutsu sen split-metodia:

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

Valinnainen toinen argumentti rajoittaa jakojen määrää:

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

2.35.6. Kääntäminen uudelleenkäyttöä varten

Jos sama kuvio ajetaan monta kertaa – silmukassa tai kuumassa funktiossa – käännä se kerran ja käytä käännettyä oliota uudelleen:

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

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

Metodien pattern.match() ja pattern.search() kutsuminen käännetylle oliolle vastaa moduulitason funktioita mutta ohittaa uudelleenkäännöskustannuksen jokaisella kutsulla.

2.35.7. Kuviot, jotka eivät täsmää mihinkään

Kolme kuviota erityisesti jää kehittäjiltä huomaamatta:

  • .* täsmää tyhjään merkkijonoon. re.search(r'.*', s).group(0) palauttaa '' millä tahansa syötteellä.

  • Kuvio, jossa on karkaamaton erikoismerkki, on syntaksivirhe. re.compile(r'cost: $5') nostaa virheen ValueError, koska $ tarkoittaa ”merkkijonon loppua”. Käytä muotoa r'cost: \$5'.

  • Piste . ei täsmää rivinvaihtoon. Täsmätäksesi rivinvaihtojen yli kirjoita kuvio käsittelemään ne eksplisiittisesti merkinnällä [\s\S] tai syötä yksi rivi kerrallaan.

Näillä osilla kuvio voi täsmätä lähes mihin tahansa tekstin kiinteämuotoiseen viipaleeseen. Rakenteisen datan poimiminen takaisin täsmäyksestä vaatii kaappaavia ryhmiä.