w_begdiv(defs) w_title(Säännöllisistä lausekkeista) w_author(Panu A. Kalliokoski) w_date($ $Date: 2004/02/05 20:18:37 $ $) w_language(finnish) char_coding: utf8 define(`w_verb', `ifelse(w_outputfmt,latex,`\verb"$1"',`w_literal(`$1')')') w_enddiv(defs)dnl ! Yleistä /Säännölliset lausekkeet/ (regular expressions) ovat voimakas työkalu tekstin etsintään eli poimintaan, muokkaukseen ja erityisesti yksinkertaisten muunnosten tekemiseen. Niihin törmää Unixissa joka puolella, ja koska niiden peruskäyttö on verrattain yksinkertaista, opettelun hyöty-vaivasuhde on melko hyvä. :) Säännöllisistä lausekkeista puhutaan sen verran paljon erilaisissa ohjeissa ja muissa, että niille on tullut paljon lempinimiä, kuten /regexp/, /regex/ tai vielä lyhyempi /RE/. Käytän tässä johdannossa lyhyyden vuoksi nimitystä /SL/. SL:ista on olemassa kaksi (tai kolme) eri varianttia, "perinteiset" SL:t, "laajennetut" SL:t (vaikka oikeasti niiden toiminnallisuus on jotakuinkin sama ja lähinnä merkinnät ovat toiset), sekä uutena ryhmänä julmasti laajennetut "Perl-yhteensopivat" SL:t. Tässä tutustumme perinteisiin SL:siin, muun muassa siksi, että ''grep'' ja ''sed'' tukevat niitä sellaisinaan (''grep'' myös laajennettuja ''-e''-valitsimella) ja koska niissä on vähiten "maagisia" merkintöjä. Linuxeissa on yleensä SL:ita kuvaava ''man''-sivu jonka saapi näkyviin komennolla ''man 7 regex''. !! Mitä SL:t sitten ovat ja mihin niitä käytetään? SL:t ovat tekstinpätkiä kuvaavia hahmoja, joita sovitetaan syötetekstiin. Esimerkiksi SL ''jotain'' on hyvin yksinkertainen SL, joka täsmää vain tekstinpätkään "jotain". Toisaalta SL ''foo.*bar'' on monimutkaisempi SL, joka täsmää mihin tahansa tekstiin, jossa on ensin pätkä "foo", sitten välissä mitä tahansa kirjaimia kuinka monta tahansa kappaletta (ml. 0), ja sen jälkeen "bar", eli esimerkiksi tekstinpätkiin "foonizbar", "fooabar", "foobar" ja "foomettofoo a mezin bar". Mitä sille täsmäävälle tekstille sitten tehdään? Se riippuu siitä, missä yhteydessä SL:tta käytetään. Tutustumme kahteen ohjelmaan, joissa voi käyttää SL:ita: ''grep'':: tekee tekstistä poimintoja SL:den perusteella. Oletuksena (siis ilman mitään valitsimia) tulostaa syötteestä rivit, joiden sisällöstä jokin kohta täsmää kyseiseen SL:seen. Valitsimella ''-o'' tulostaa syötteestä vain SL:seen täsmänneet pätkät (ei koko riviä). Huomaamme siis, että tähänastinen ''grep'':n käyttömme on ollut erityistapauksia: olemme käyttäneet vain sellaisia säännöllisiä lausekkeita, jotka täsmäävät vain yhdenlaiseen tekstiin, kuten ''jotain'' yllä. Esimerkiksi komento w_verb(`grep w_apo`'foo.*bar`'w_apo tiedosto.txt') näyttää ''tiedosto.txt'':sta ne linjat, joilla on ensin jossain kohtaa "foo" ja sen perässä jossain kohtaa "bar". ''sed'':: Muuntaa tekstiä SL:den perusteella. (Itse asiassa ''sed'' on pieni ohjelmointikieli, jolla voi tehdä kaikenlaista muutakin, mutta tämä on sen ylivoimaisesti yleisin käyttö.) Tämä tapahtuu ''sed'':n alakäskyn ''s'' avulla. ''s'' korvaa syötteestä SL:seen täsmänneet osat jollain toisella (annetulla) tekstillä, mutta päästää syötetekstin muuten läpi muuttumattomana. Esimerkiksi komento w_verb(`sed w_apo`'s/taivas/maa/g`'w_apo tiedosto.txt') näyttää tiedosto.txt:n sisällön siten, että jokainen "taivas"-sana (tai sanan osa) on korvattu "maa"-sanalla. !! SL:ista teoreettisemmin Jokainen SL määrittää tietyn (mahdollisesti äärettömän) joukon merkkijonoja, eli ne merkkijonot, jotka "täsmäävät" kyseiseen SL:seen. Tällaista merkkijonojen joukkoa kutsutaan perinteisesti /kieleksi/. SL:den avulla pystyy määrittämään kieliä, jotka kuuluvat /säännöllisten kielten/ luokkaan (siksi SL:ita sanotaan SL:iksi). Kieliä, joiden järkevien ilmausten joukkoa ei pysty määrittämään SL:lla, ovat esimerkiksi useimmat ohjelmointikielet (jotka enimmäkseen kuuluvat /kontekstittomien kielten/ luokkaan) ja luonnolliset kielet (kuuluvat /kontekstillisten kielten/ luokkaan). Käytännössä tämä tarkoittaa, että on paljon kieliä, joita ei pysty kuvaamaan SL:illa ollenkaan, mutta silti niillä pystyy kuvaamaan hyvin monia asioita. SL:t ovat hyvin tiivis merkintätapa säännöllisille kielille. Niiden intuitiivista ymmärtämistä helpottaa se, että ne muistuttavat täsmäämiään merkkijonoja: esimerkiksi ''kai.aa'' vastaa kaikkia merkkijonoja, joissa on ensin "kai", sitten mikä tahansa yksi kirjain, sitten "aa", esimerkiksi "kaivaa" ja "kaitaa". SL:den perustavanlaatuinen ominaisuus onkin, että niissä _suurin osa kirjaimista vastaa itseään_. Esimerkiksi ''a'' täsmää tekstiin "a". SL:den tiiviys on myös haitta. Lausekkeiden monimutkaistuessa niihin alkaa tulla paljon näitä maagisia merkkejä jotka eivät esitä itseään vaan jotain yleisempää (kuten piste ''.'' tai tähti ''*'') ja niistä tulee nopeasti hyvin kryptisiä ja vaikeita lukea. SL:ita onkin kutsuttu silloin tällöin välimerkkitehtaan räjähdykseksi, koska suurin osa maagisista merkeistä on välimerkkejä. Mutta joo. Edetkäämme katsomaan, mitä rakenteita niissä säännöllisissä lausekkeissa on käytettävissä. ! Perus-SL:t !! Itsestäänselvyydet Nämä asiat useimmat ihmiset tajuavat luonnostaan, mutta niiden sanominen erikseen on silti hyvästä. SL:den perusrakennuspala on _kirjain_. Jokainen tavallinen (siis ei-maaginen) kirjain on SL, joka täsmää itseensä. SL:den perusrakennustapa on /katenaatio/ eli peräkkäin yhdistäminen. SL, jossa on peräkkäin SL $s_1$ ja SL $s_2$ täsmää tekstiin, jossa on ensin jotain, joka täsmää SL:seen $s_1$ ja heti sen perässä jotain, joka täsmää SL:seen $s_2$. Esimerkiksi: SL ''ka'' täsmää tekstiin "ka", koska sen ensimmäinen osa ''k'' on SL, joka täsmää kirjaimeen k ja toinen osa ''a'' on SL, joka täsmää kirjaimeen a. Edelleen ''kan'' täsmää tekstiin "kan" koska sen ensimmäinen osa ''ka'' on SL, joka täsmää tekstiin "ka" ja toinen osa ''n'' on SL, joka täsmää kirjaimeen n. Oliko tarpeeksi perusteellista? Tämä on siis "syy" siihen, miksi kaikki maagisia merkkejä sisältämättömät SL:t täsmäävät samannäköiseen tekstiin. !! Ensimmäiset maagiset merkit Esimerkkimme ''foo.*bar'' yllä sisältää kaksi maagista merkkiä: pisteen ja tähden. Molemmat ovat käytettävissä erikseen, vaikka usein ne näkeekin tällä tavoin yhdessä. Mitä ne siis tarkoittavat? ''.'':: Tämä on oikeastaan jo selitetty. Se täsmää mihin tahansa kirjaimeen. Syötetekstissä voi siis pistettä vastaavalla kohdalla olla mikä tahansa merkki. Tämä on yksinkertaisin /merkkiluokka/ (siihen kuuluvat kaikki merkit). ''*'':: Tähti on /operaattori/, eli se muuttaa lähintä edeltävää SL:tta. Tähti sanoo, että sen sijaan, että edeltävään SL:seen täsmäävä merkkijono pitäisi löytyä syötteestä kerran, siitä saakin löytyä kuinka monta tahansa (mukaanlukien ei yhtään) peräkkäistä edeltävään SL:seen täsmäävää merkkijonoa. Tähti toimii aivan hyvin myös tavallisten kirjainten muuntimena. Esimerkiksi ''telefo*ni'' täsmää sanoihin "telefoni", "telefoooooni" ja "telefni", muttei sanaan "telefuuni". Lisää esimerkkejä: w_beg(table,l,p,p) SL || täsmää || ei täsmää // ---- ''j..urtt*i'' || "jokurtti", "jugurti", "jYÅurttttttti" || "yogurtti", "jukuri" // ''Jarmo .*Puntti'' || "Jarmo L. za-Puntti", "Jarmo Puntti" || "Jarmo-san Puntti", "Jarm Puntti" // w_end(table) !! Tarkemmat merkkiluokat Jorkutthi-esimerkissämme yllä ''.'' oli tarpeettoman laaja luokka. Riittäisi, että kirjain voisi olla jokin muutamasta yleisestä variantista. Hakasulkeilla (''['' ja '']'') voi määrittää merkkiluokkia, joihin kuuluu tietyt kirjaimet. Esimerkiksi SL ''[aieouyäö]'' täsmää mihin tahansa (suomen kielen) vokaaliin ja SL ''t[ei][ae]'' täsmää sanoihin "tie", "tee", "tea" ja "tia". Edelleen SL ''[Jj]oskus'' täsmää sanaan "joskus" riippumatta, onko se kirjoitettu isolla vai pienellä alkukirjaimella. Hakasulkeiden sisällä voi käyttää myös ''tr''-mäisiä välejä, joista yleisimmät ovat ''a-z'' (kaikki pienet kirjaimet a:sta z:an), ''A-Z'' ja ''0-9'' (kaikki numerot). Siispä SL ''[a-zåäö]'' täsmää mihin tahansa suomen kielen pieneen kirjaimeen ja SL ''[a-zåäö]*'' minkä tahansa mittaiseen rimpsuun niitä (esim. kokonaan pienellä kirjoitettuun sanaan). Hakasulkeilla voi muodostaa myös /käänteisluokkia/ eli luokkia, johon kuuluvat kaikki _muut_ kuin luetellut kirjaimet. Käänteisluokka merkitään w_verb([^kirjaimia...]) eli esimerkiksi SL w_verb([^A-Za-z]) täsmää mihin tahansa merkkiin, joka ei ole englannin kielen sanakirjain. Käänteisluokat ovat siitä merkittäviä, että niillä selviää tietystä hankalasta tilanteesta. Tähtirakenne ''*'' on nimittäin siitä hankala, että usein joudutaan tilanteeseen, jossa sillä on useampia laillisia täsmättäviä pituuksia. Esimerkiksi jos poimimme SL:tta ''foo.*bar'' ja syöte on "frobnize foo in bar for every bar", laillisia poimintoja ovat "foo in bar" sekä "foo in bar for every bar". Perinteisissä SL:issa useammista vaihtoehdoista valitaan aina pisin. Tämä ei kuitenkaan usein ole se, mitä halutaan: jos yritämme poimia sulkeissa olevia asioita SL:lla ''(.*)'', syöte "joskus (muttei usein) menee pieleen (vahinko!)" tuottaa väärän tuloksen "(muttei usein) menee pieleen (vahinko!)", kun olisi pitänyt tuottaa kaksi poimintoa "(muttei usein)" ja "(vahinko!)". Ratkaisu on korvata ''.*'' SL:sta lausekkeella, joka ei sovitu sulkeviin sulkeisiin. ''([w_ct)]*)'' tuottaa oikean tuloksen. Yleensäkin tämä tapa kieltää erikseen ''*'':n tuottamasta rimpsusta tietyt merkit on hyvin yleinen. !! Rivin ja sanan alku ja loppu Olen hiukan yrittänyt vastustaa kiusausta sanoa esimerkiksi, että SL ''poika'' täsmää _sanaan_ "poika". Tässä SL:ssahan ei ole mitään, mikä varsinaisesti pakottaisi täsmäävän tekstin olevan itsenäinen sana: myös sanasta "poikamies" täsmää siihen alkuosa ja merkkijonosta "hoopoikarus" keskiosa. Perinteinen ratkaisu tähän on ottaa sanaa seuraava ja edeltävä merkki mukaan SL:seen, esimerkiksi jommallakummalla seuraavista tavoista: * w_verb(`[ ,.-]poika[ ;:,.-]') * w_verb([^-a-zåäö]poika[^-a-zåäö]) (viiva pitää panna paikkaan, jossa sitä ei voi tulkita välin merkinnäksi) Tässä on se vika, että rivin alussa ja lopussa oleva sana jää poimimatta, koska sitä ei edellä / seuraa mikään merkki. Edelleen tähän perinteinen ratkaisu on ollut lisätä rivin alkuun ja loppuun esim. välilyönti ennen ''grep'':lle antamista. Tämä on sotkuista. Ongelmaan on keksitty erilaisia ratkaisuja, kuten maagisia merkintöjä, jotka täsmäävät sanojen alkuihin tai loppuihin, tai esim. ''grep'':n valitsin ''-w'', joka kertoo, että annetun SL:n tulee täsmätä itsenäiseen sanaan. Itse asiassa Linuxeissa sekä ''grep'' että ''sed'' ymmärtävät merkinnät w_verb(\<) ja w_verb(\>), jotka täsmäävät sanan alkuun ja loppuun. Itse asiassa tarkempaa olisi sanoa, että ne täsmäävät tyhjään merkkijonoon sanan alussa tai lopussa, koska ne eivät siis täsmätyssä tekstissä vastaa yhtäkään kirjainta, edellyttävätpähän vain, että kyseisellä kohdalla on sanan alku / loppu. Mutta näiden merkintöjen tarjolla olemisesta ei ole takuita kaikissa SL-varianteissa. Rivin alulle ja lopulle on sen sijaan olemassa jokikisessä SL-variantissa toimivat maagiset merkit: ''w_ct'':: Täsmää rivin alkuun. Esimerkiksi ''w_ct-1)'' täsmää merkkijonoon "-1)" rivin alussa ja komennolla w_verb(`sed w_apo`'s/^[ ]*//`'w_apo tiedosto.txt') voi poistaa välilyönnit kaikkien rivien alusta (hakasulkeet eivät ole välttämättömät mutta saavat SL:n mielestäni helpommaksi lukea). ''w_dol'':: Täsmää rivin loppuun. Esimerkiksi komennolla w_verb(`sed w_apo`'s/$/!/`'w_apo tiedosto.txt') voi lisätä huutomerkin jokaisen rivin loppuun (sillä se korvaa tyhjän merkkijonon rivin lopusta huutomerkillä). Tällä komennolla voi poimia rivit, joiden neljänneksi viimeinen kirjain on "a": w_verb(`grep w_apo`'a...$`'w_apo tiedosto.txt') Vielä yksi esimerkki: seuraava komento poimii rivit, joilla ei ole mitään muuta kuin sana "turha": {{{ grep '^turha$' tiedosto.txt }}} On muuten syytä huomata, että nämä erikoiset "paikkaehtomerkinnät" tekevät mahdolliseksi tehdä SL:ita, jotka eivät täsmää yhtään mihinkään merkkijonoon. Esimerkiksi w_verb(po\) ei voi täsmätä mihinkään, koska kahden o-kirjaimen väli ei koskaan ole sanan alku. ! Vaativammat SL:t Tähän mennessä käsitellyt ominaisuudet ovat sellaisia, että niitä tarvitsee vähän väliä ja ne ovat verrattain helppoja ymmärtää ja pähkäillä. Niillä saa yllättävän paljon aikaan ja monelle käyttäjälle ne riittävät aivan hyvin. Tässä jaksossa käsittelemme rakenteita, jotka lisäävät SL:den kuvausvoimaa huomattavasti, mutta jotka ovat vaikeatajuisempia ja edellyttävät yhdistelykykyä. !! Toisto-operaattorit Olemme käsitelleet tähän mennessä vain yhden operaattorin (maagisen rakenteen, joka muuttaa jonkin SL:n merkitystä). ''*'' on yksinkertaisin toisto-operaattori: se määrittää, että edeltävä SL saa olla syötteessä peräkkäin kuinka monta kertaa tahansa. Tämä on erikoistapaus toisto-operaattoreista, ja samoin kuin pisteen (''.'') tapauksessa, sen yleisyyttä voi rajoittaa. Rakenne w_verb(`\{n,m\}') (jossa /n/ ja /m/ ovat joitain numeroita) merkitsee, että edeltävän SL:n pitää täsmätä vähintään /n/ ja enintään /m/ peräkkäistä kertaa. Jos /m/ puuttuu, ylärajaa ei ole. Esimerkiksi: w_beg(table,l,p) SL || merkitys // ---- w_verb(`a\{3,6\}') || 3 - 6 a-kirjainta peräkkäin // w_verb(`[aeiou]\{4,\}') || vähintään 4 englannin kielen vokaalia peräkkäin // w_verb(`roc\{0,1\}k') || "rock" tai "rok" // w_verb(`^[ ]*[0-9]\{3,\}') || vähintään 3 numeroa (sisennettynä) rivin alussa // w_end(table) Huomaa myös, että seuraavat SL:t täsmäävät samoihin merkkijonoihin eli määrittävät saman kielen: # w_verb(`[abc]\{2,\}') # w_verb([abc][abc][abc]*) Laajennetuissa SL:issa on monille tyypillisille toistomäärille lyhenteitä. Niistä saapi tietoa ''man 7 regex''-sivulta. !! Sitovuusjärjestys (presedenssi) Operaattorit muuntavat aina edeltävän SL:n merkitystä. Tarvitaan keino, jolla voi kertoa, kuinka suuri osa SL:sta kuuluu operaattorin muunnettavaksi. w_verb(`\(') ja w_verb(`\)') toimivat kuten sulkeet matematiikassa ja kertovat, missä järjestyksessä SL tulkitaan. Jos halutaan pidempi kuin yhden merkin toistuva osuus, pitää sulkeet merkitä erikseen: w_beg(table,l,p) SL || täsmää // ---- w_verb(bar*) || esim. ba, bar, barrrrr // w_verb(\(bar\)*) || esim. bar, barbarbarbar ja tyhjä merkkijono // w_verb(`mus\(ta\)\{1,2\}') || musta, mustata // w_verb(mus\(t[aeiou]\)*) || esim. mus, muste, mustuti, mustotata // w_verb(`\(ha[ .h]*\)\{1,\}') || esim. hahaha, hah. hah., hahha.haha .hah. // w_end(table) Usein, kun alkaa tekemään jotain "hienoa" eli monimutkaista säännöllisillä lausekkeilla, pitää aloittaa yksinkertaisesta ja tarkentaa SL:tta pikku hiljaa saadakseen tarkempia tuloksia. Tällöin usein pitää muuttaa merkkiluokka joksikin suljelausekkeeksi. Esimerkkinä virkkeeseen täsmäävä SL (''[.]'' tarkoittaa nimenomaan pistettä (eikä mitä tahansa merkkiä), sillä hakasulkeissa piste on tavallinen kirjain eikä maaginen): w_beg(table,l,p) SL || kommentti // ---- w_verb([A-ZÅÄÖ].*[.]) || liian laaja, .* voi mennä yli virkerajasta // w_verb([A-ZÅÄÖ][^.]*[.]) || periaatteessa hyvä, mutta pisteloppuiset lyhenteet katkaisevat virkkeen // w_verb([A-ZÅÄÖ]\([^.]\([.] [a-zåäö]\)*\)*[.]) || vielä parempi // w_end(table) Tässä siis toistettava SL monimutkaistettiin siten, että virkerimpsussa saa olla (ei-pisteiden lisäksi) kohtia, joissa on piste, välilyönti ja sitten pieni kirjain. Ei toimi vieläkään esim. syötteelle "toht. Nurmi" mutta välttää. Toinen esimerkki: rivi, jolla on kolmesta viiteen a-kirjainta: w_beg(table,l,p) SL || kommentti // ---- w_verb(`^a\{3,5\}$') || nyt rivillä ei voi olla mitään muuta kuin a-kirjaimia // w_verb(`^\(a.*\)\{3,5\}$') || ei toimi, piste täsmää myös a-kirjaimeen // w_verb(`^\(a[^a]*\)\{3,5\}$') || edellyttää ensimmäisen a:n rivin alkuun // w_verb(`^[^a]*\(a[^a]*\)\{3,5\}$') || toimii // w_end(table) !! Takaisinviittaukset Tämä hämmästyttävän monipuolinen ominaisuus puuttuu laajennetuista SL:ista (joten oikeasti ne ovat myös kavennettuja). Syyt tähän ovat hyvin teknisiä ja liittyvät SL:den toteuttamiseen ohjelmissa. Vaikka periaatteessa takaisinviittauksille on helppo keksiä käyttökohteita, yleensä niitä ei tarvitse, ja esittelenkin ne sen takia, että ne helpottavat ''sed'':n kehittyneiden tekstikorvausten ymmärtämistä. Toisto-operaattorit ovat edelleen hyvin sallivia. Ne eivät oikeasti edellytä toistoja _syötetekstissä_ vaan pelkästään toistuvia täsmäyksiä edeltävään lausekkeeseen. Esimerkiksi ''[klo]*'' täsmää, paitsi merkkijonoihin "kkkk", "lll", "ooooo" ja tyhjä, myös merkkijonoon "olkkolokoo". Yleensä tämä on juuri se, mitä haluammekin. Mutta mitä, jos haluamme löytää oikeita toistoja tekstistä? Tätä varten takaisinviittaukset ovat. Rakenteella w_verb(\n) (jossa /n/ on jokin luku) voi viitata aiempien suljeilmausten (joita ympäröi w_verb(`\(') ja w_verb(`\)')) sisältöön. Esimerkiksi w_verb(\4) viittaa tekstiin, joka syötteessä täsmäsi neljänsien sulkeiden (laskien avaavia sulkeita vasemmalta lähtien) sisältämään SL:seen. w_beg(table,l,p) SL || täsmää // ---- w_verb(\([klo]\)\1) || kk, ll, oo // w_verb(\([klo]\)\1*) || esim. k, lllllll, oooo // w_verb(\(.\)\1\1) || esim. ooo, aaa, ))) // w_verb(\([^a-z]\)[a-z]*\1) || sanaan, jota ennen ja jonka jälkeen on sama merkki // w_verb(`\([a-zåäö]\{1,\}\) \1') || kahdesti peräkkäin tulevaan sanaan // w_end(table) ! ''sed'':n käyttö !! Yksinkertaiset tekstikorvaukset Yksinkertaiset tekstikorvaukset on helppo tehdä ''sed'':lla. ''sed'':lle annetaan argumentiksi (hipsuissa, yleensä) komento muotoa ''s/mitä/mihin/valitsimia'', jossa /mitä/ on SL, johon täsmäävä teksti korvataan, /mihin/ on teksti, jolla se korvataan, ja /valitsimet/ ovat ''sed'':n ''s''-komennon toimintaa muuttavia kirjaimia. Yleisin näistä on ''g'', joka pyytää tekemään kaikki mahdolliset muutokset joka rivillä (muuten ''sed'' tekee vain ensimmäisen). w_beg(table,l,l,l) ''s''-komento || syöte || tulos // ---- ''s/foo/bar/g'' || kafoo on foo || kabar on bar // ''s/aa*/A/g'' || laakso ja jakso || lAkso jA jAkso // ''s/a/e/'' || paalu || pealu // ''s/([w_ct)]*)//g'' || (Jos) on tärkeää (niin) || on tärkeää // ''s/[yj][ou][gk]u/jugu/'' || yokurtti || jugurtti // w_end(table) Itse asiassa ''s''-komennon osia ei tarvitse erottaa toisistaan juuri kauttaviivoilla --- mikä tahansa merkki käy. Kauttaviiva on perinteinen valinta, mutta jos SL tai korvaava teksti sisältää kauttaviivoja, kannattaa valita toinen merkki. !! Monimutkaisemmat tekstikorvaukset ''s''-komennon /mihin/-osa voi sisältää takaisinviittauksia. Näin SL:n täsmäämästä tekstistä voi poimia osia ja uudelleenjärjestää ne tai lisäillä niihin jotain. Lisäksi koko täsmänneen tekstin saa ''w_amp''-merkistä. w_beg(table,l,l,l) ''s''-komento || syöte || tulos // ---- w_verb(s/^\(...\)\(.\)/\2\1/) || Paavo Kauppi || vPaao Kauppi // w_verb(`s/\([A-Z][a-z]*\) \(.*\)/\2, \1/') || Paavo Kauppi || Kauppi, Paavo // w_verb(`s/[a-zåäö]\{1,\}/w_apo&w_apo/g') || juu ei siis || 'juu' 'ei' 'siis' // w_verb(`s/\([a-z]\)\([a-z]*\)@.*/\1. \2/') || pkalliok@ling || p. kalliok // w_end(table) Tällä tavalla saa usein esim. olennaisesti vähennetyksi työtä, jos pitää muuntaa jokin epämääräisessä muodossa oleva yhteystietoluettelo järkevään muotoon. Tai vaihtaa sanoja tiedostoista.