Ero sivun ”Säännöllinen lauseke” versioiden välillä

Linux.fista
Siirry navigaatioon Siirry hakuun
(→‎Merkkiluokat: []: LC_COLLATE, [:mikälie:])
(ehostus)
Rivi 1: Rivi 1:
'''Säännöllinen lauseke''' (engl. '''regular expression''', '''regexp''') on yksinkertainen merkkijonokieli, joka voi joko ''vastata'' tai ''olla vastaamatta'' jotain toista merkkijonoa.  Linux- ja Unix-järjestelmissä säännöllisiä lauskkeista on suurta hyötyä [[komentorivi]]ä käytettäessä merkkijonojen etsi/korvaa-toiminnoissa. Niiden voidaankin sanoa olevan [[putki]]ttamiseen yhdistettynä tärkeimpiä työkaluja, joita komentorivin edistynyt käyttö edellyttää.
'''Säännöllinen lauseke''' (engl. '''regular expression''', '''regexp''') on yksinkertainen merkkijonokieli, joka voi joko ''vastata'' tai ''olla vastaamatta'' jotain toista merkkijonoa.  Linux- ja Unix-järjestelmissä säännöllisiä lauskkeista on suurta hyötyä [[komentorivi]]ä käytettäessä esimerkiksi merkkijonojen etsimis- ja korvaustoiminnoissa. Niiden voidaankin sanoa olevan [[putki]]ttamiseen yhdistettynä tärkeimpiä työkaluja, joita komentorivin edistynyt käyttö edellyttää.


Tämän säännöllisten lausekkeiden esityksen perusteella lausekkeet ymmärtää, mutta kaikissa yhteyksissä syntaksi ei ole sama. Kolme syntaksityyppiä on yleisesti käytössä: tavallinen ("basic"), laajennettu ("extended") ja [[perl]]-tyyppinen. Ensinmainitussa useammat merkit vastaavat itseään ja esimerkiksi "+" erikoismerkityksessään esitetään "\+". Alla esitetään uudempi laajennettu syntaksi. Varsinkin usealla koneella käytettäviä skriptejä kirjoitettaessa pitää olla huolellinen siinä, mitä lajennoksia käyttää.
Tässä artikkelissa on esitetty johdatus säännöllisiin lausekkeisiin siten, miten komentorivityöklu <tt>[[grep]]</tt> ne ymmärtää ''laajennettussa'' (engl. extended) tilassa. Kaikissa säännöllisten lausekkeiden toteutuksissa syntaksi ei kuitenkaan ole aivan sama. Perusoperaattorit (<tt>*</tt>, <tt>+</tt>, <tt>?</tt>, sulut) ovat kuitenkin ''universaaleja'', eli kaikki toteutukset tukevat niitä.


Säännöllisiä lausekkeita ei pidä sekoittaa tiedostonimi-jokereihin ("glob"), joilla on osittain sama toiminnallisuus (vrt muoto "*" ja ".*").
Säännöllisistä lausekkeista on yleisesti käytössä kolme tyyppiä: ''tavallinen'' (engl. basic), laajennettu (engl. extended) ja ''[[Perl]]-tyyppinen''. Varsinkin useassa järjestelmässä käytettäviä [[skripti|skriptejä]] kirjoitettaessa pitää olla huolellinen, mikäli käyttää tavallisesta poikkeavia ilmaisuja. Tässä ohjeessa esitetty syntaksi on yhteensopiva Linuxin [[GNU]]-grepin laajennetun syntaksin kanssa jollei toisin mainita.
 
Säännöllisiä lausekkeita ei pidä sekoittaa tiedostonimi-jokereihin ([[glob]]), joilla on samankaltainen toiminnallisuus (vrt. <tt>*</tt> ja <tt>.*</tt>).


== Teoriaa ==
== Teoriaa ==
Tietojenkäsittelytieteessä puhutaan [[wikipedia:fi:säännöllinen kieli|säännöllisestä kielestä]], joka voidaan tunnistaa [[wikipedia:fi:äärellinen automaatti|äärellisellä automaatilla]]. Säännöllisiä lausekkeita käytetään myös monissa ohjelmointikielissä (mm. Perl, Java, Python, ECMAScript). Mille tahansa [[wikipedia:en:Turing complete|Turing-täydelliselle]] ohjelmointikielelle voidaan kuitenkin aina kirjoittaa säännöllisten lausekkeiden [[wikipedia:fi:Ohjelmointikielen tulkki|tulkki]].
Tietojenkäsittelytieteessä puhutaan [[wikipedia:fi:säännöllinen kieli|säännöllisestä kielestä]], joka voidaan tunnistaa [[wikipedia:fi:äärellinen automaatti|äärellisellä automaatilla]]. Säännöllisten lauskkeiden historia juontaa juurensa siten 50- ja 60-luvuille [[wikipedia:fi:tietojenkäsittelytiede|tietojenkäsittelytieteen]] syntyaikohiin. [[Unix]]-järjestelmissä säännöllisiä lausekkeita on voinut hyödyntää aina ensimmäisistä versioista lähtien. Ensimmäinen grep-ohjelma kirjoitettiin ilmeisesti vuonna 1973.<sup>[http://www.columbia.edu/~rh120/ch001j.c11]</sup>
 
Säännöllisiä lausekkeita voi hyödyntää monissa ohjelmointikielissä (mm. Perl, Java, Python, ECMAScript). Mille tahansa [[wikipedia:en:Turing complete|Turing-täydelliselle]] ohjelmointikielelle voidaan myös aina kirjoittaa säännöllisten lausekkeiden [[wikipedia:fi:Ohjelmointikielen tulkki|tulkki]].


== Johdatus säännöllisiin lausekkeisiin ==
== Johdatus säännöllisiin lausekkeisiin ==
Rivi 110: Rivi 114:


Sivuhuomatuksena todettakoon, että yllä olevan perusteella lyhyemmät operaattorit <tt>*</tt>, <tt>+</tt> ja <tt>?</tt> voitaisiin aina korvata ilmaisuilla <tt>{0,}</tt>, <tt>{1,}</tt> ja <tt>{0,1}</tt> vastaavasti. {m,n}-muoto on kuitenkin uudempi laajennos, joka ei toimi kaikissa ohjelmassa.
Sivuhuomatuksena todettakoon, että yllä olevan perusteella lyhyemmät operaattorit <tt>*</tt>, <tt>+</tt> ja <tt>?</tt> voitaisiin aina korvata ilmaisuilla <tt>{0,}</tt>, <tt>{1,}</tt> ja <tt>{0,1}</tt> vastaavasti. {m,n}-muoto on kuitenkin uudempi laajennos, joka ei toimi kaikissa ohjelmassa.
'''Huom!''' Operaattori <tt>{n,m}</tt> ei ole tuettu kaikissa säännöllisten lausekkeiden toteutuksissa. GNU grep tukee niitä laajennetussa tilassa (<tt>egrep</tt>).


===== Huomautuksia =====
===== Huomautuksia =====
Rivi 237: Rivi 243:
mutta ei
mutta ei
  sepe
  sepe


====Merkkiluokat: <tt>[]</tt>====
====Merkkiluokat: <tt>[]</tt>====
Rivi 253: Rivi 258:
  [thlTHL]upu
  [thlTHL]upu


Hakasulkujen välissä voidaan myös määritellä [[locale|LC_COLLATE]] mukaisen aakkoston mukaisia välejä väliviivalla <tt>-</tt>. Tällöin  
Hakasulkujen välissä voidaan myös määritellä [[lokaali]]n aakkoston mukaisia välejä väliviivalla <tt>-</tt>. Tällöin  
  19[4-9][0-9]|20[0-9][0-9]
  19[4-9][0-9]|20[0-9][0-9]
vastaa kaikkia vuosilukuja välillä 1940-2099. Samoin  
vastaa kaikkia vuosilukuja välillä 1940-2099. Samoin  
Rivi 267: Rivi 272:
  '''Osasto '''J
  '''Osasto '''J


”Osasto b” ja ”Osasto È” saattavat sisältyä, riippuen lokaalista.
On huomioitavaa, että myös kohteet ”Osasto b” tai ”Osasto È” saattavat vastata yllä esitettyä lausketta joissain [[lokaali|lokaaleissa]].
 
Tiettyjä merkkiluokkia on määrätty ennakkoon, esimerkiksi
*[:digit:] vastaa mitä tahansa numeroa
*[:alpha:] vastaa mitä tahansa kirjainta
*[:alnum:] vastaa mitä tahansa kirjainta tai numeroa
Muita tällaisia ovat [:cntrl:], [:lower:], [:upper:], [:space:], [:blank:], [:punct:], [:print:], [:graph:] ja [:xdigit:]. Esimerkiksi <nowiki>[[:upper:]]</nowiki> vastaa C-lokaalilla (LC_CTYPE=C) luokkaa [A-Z]. Merkkiluokkia käytettäessä on huomattava, että käytetty lokaali vaikuttaa ratkaisevasti siihen, mitä merkkejä luokkaan sisältyy. Siksi niitä ei voi käyttää tietoturvatarkistuksiin muuta kuin ennaltamäärätyllä, hyvin ymmärretyllä, lokaalilla.


Jos merkki <tt>-</tt> halutaan sisällyttää hakasulkuilmaisuun, se jätetään viimeiseksi. Esim.
Jos merkki <tt>-</tt> halutaan sisällyttää hakasulkuilmaisuun, se jätetään viimeiseksi. Esim.
Rivi 306: Rivi 305:


Grepin hyväksymissä säännöllisissä lausekkeissa merkin <tt>^</tt> voi sisällyttää hakasulkuilmaukseen laittamalla sen miksi tahansa muuksi merkiksi, kuin hakasulkujen ensimmäinen merkki. Tämä ei pidä välttämättä paikkaansa kuitenkaan kaikilla säännöllisten lausekkeiden toteutuksilla, vaan hattumerkin eteen on mahdollisesti laitettava pako-operaattori <tt>\</tt>.
Grepin hyväksymissä säännöllisissä lausekkeissa merkin <tt>^</tt> voi sisällyttää hakasulkuilmaukseen laittamalla sen miksi tahansa muuksi merkiksi, kuin hakasulkujen ensimmäinen merkki. Tämä ei pidä välttämättä paikkaansa kuitenkaan kaikilla säännöllisten lausekkeiden toteutuksilla, vaan hattumerkin eteen on mahdollisesti laitettava pako-operaattori <tt>\</tt>.
=====POSIX-merkkiluokat=====
Joitain erikoismerkkiluokkia on määrätty ennakkoon grepissä. Esimerkiksi
*<tt><nowiki>[:digit:]</nowiki></tt> vastaa mitä tahansa numeroa
*<tt><nowiki>[:alpha:]</nowiki></tt> vastaa mitä tahansa kirjainta
*<tt><nowiki>[:alnum:]</nowiki></tt> vastaa mitä tahansa kirjainta tai numeroa
*<tt><nowiki>[:space:]</nowiki></tt> vastaa mitä tahansa tyhjää (esim. välilyönti, tabulaattori) merkkiä
Muita tällaisia ovat <tt><nowiki>[:lower:]</nowiki></tt>, <tt><nowiki>[:upper:]</nowiki></tt>, <tt><nowiki>[:xdigit:]</nowiki></tt>, <tt><nowiki>[:blank:]</nowiki></tt>, <tt><nowiki>[:punct:]</nowiki></tt>, <tt><nowiki>[:print:]</nowiki></tt>, <tt><nowiki>[:cntrl:]</nowiki></tt> ja <tt><nowiki>[:graph:]</nowiki></tt>.
Merkkiluokat tulee laittaa lisäksi ulompien hakasulkujen <tt>[]</tt> sisään. Esimerkiksi
<nowiki>[[:upper:]]</nowiki>+
joka vastaa yleensä ilmaisua
[A-Z]+
eli mielivaltainen (vähintään yksi) määrä isoja kirjaimia A-Z.
'''Huom!''' Merkkiluokkia käytettäessä on huomioitava, että käytetty lokaali vaikuttaa ratkaisevasti siihen, mitä merkkejä luokkaan sisältyy. Siksi niitä ei voi käyttää tietoturvatarkistuksiin muuta kuin ennalta tiedetyillä lokaaleilla.


====Pako-operaattori: <tt>\</tt>====
====Pako-operaattori: <tt>\</tt>====
Rivi 395: Rivi 411:
  $ echo moi | egrep 'm(o|a)i'
  $ echo moi | egrep 'm(o|a)i'
  moi
  moi
Esimerkissä käytettiin komentoa egrep. Se on oikopolku grepin [[valitsin|valitsimelle]] <tt>-E</tt>, joka ottaa säännöllisten lausekkeiden laajennetun tuen käyttöön. Normaalissa käytössä (pelkkö komento <tt>grep</tt>) operaattorit <tt>? + {} | (</tt> ja <tt>)</tt> eivät ole käytettävissä, paitsi asettamalla niiden eteen pako-operaattori <tt>\</tt>.


===== Laajennetut säännölliset lausekkeet: egrep =====
Esimerkissä käytettiin komentoa <tt>egrep</tt>. Se on oikopolku grepin [[valitsin|valitsimelle]] <tt>-E</tt>, joka ottaa säännöllisten lausekkeiden laajennetun (engl. extended) tuen käyttöön. Normaalissa käytössä (pelkkä komento <tt>grep</tt>) operaattorit <tt>? + {} | (</tt> ja <tt>)</tt> eivät ole käytettävissä, paitsi asettamalla niiden eteen pako-operaattorin <tt>\</tt>.
===== Prosessoitu tuloste: awk =====
Jos ylläolevassa prosessilistausesimerkissä halutaan tulostaa pelkät prosessien [[PID]]-numerot, voidaan käyttää [[awk]]-työkalua seuraavasti:
Jos ylläolevassa prosessilistausesimerkissä halutaan tulostaa pelkät prosessien [[PID]]-numerot, voidaan käyttää [[awk]]-työkalua seuraavasti:
  $ ps -ef | awk '/^qmail/ { print $2 }'
  $ ps -ef | awk '/^qmail/ { print $2 }'
Rivi 415: Rivi 434:


== Laajennukset ==
== Laajennukset ==
Yllä esitetty on hyvä lähtökohta säännöllisiin lausekkeisiin, ja kaikki modernit [[POSIX]]-yhteensopivat toteutukset tukevat mainittuja operaattoreita ja erikoismerkkejä. Monissa ohjelmointikielissä on tehty laajennuksia tähän, mutta esim. [[grep]] ei tue seuraavia.  
Lukuunottamatta operaattoria <tt>{n,m}</tt> ja POSIX-luokkia (<tt>[[:luokka:]]</tt> jne.) yllä esitetty on pitkälti universaalisti tuettua säännöllisten lausekkeiden eri toteutuksissa. Monissa toteutuksissa on kuitenkin tehty laajennuksia tähän, mutta esim. [[grep]] ei tue seuraavia.  


=== Pakoluokat ===
=== Pakoluokat ===
Jos halutaan kirjoittaa lause, joka jossain kohtaa vastaa mitä tahansa numeromerkkiä, voidaan kirjoittaa hakasulkuilmaus <tt>[0-9]</tt>. Tälläisille yleisesti käytetyille luokille on kuitenkin olemassa laajennuksissa helpompia nimiä. Kutsutaan näitä ''pakoluokiksi''.
Jos halutaan kirjoittaa lauseke, joka jossain kohtaa vastaa mitä tahansa numeromerkkiä, voidaan kirjoittaa hakasulkuilmaus <tt>[0-9]</tt>. Tälläisille yleisesti käytetyille luokille on kuitenkin olemassa laajennuksissa helpompia nimiä. Kutsutaan näitä ''pakoluokiksi''. Ne muistuttavat läheisesti [[#POSIX-merkkiluokat|POSIX-merkkiluokkia]], mutta ovat lyhyempiä.


{| style="border-style: solid; border-collapse: collapse" border="1"
{| style="border-style: solid; border-collapse: collapse" border="1"

Versio 17. helmikuuta 2010 kello 19.18

Säännöllinen lauseke (engl. regular expression, regexp) on yksinkertainen merkkijonokieli, joka voi joko vastata tai olla vastaamatta jotain toista merkkijonoa. Linux- ja Unix-järjestelmissä säännöllisiä lauskkeista on suurta hyötyä komentoriviä käytettäessä esimerkiksi merkkijonojen etsimis- ja korvaustoiminnoissa. Niiden voidaankin sanoa olevan putkittamiseen yhdistettynä tärkeimpiä työkaluja, joita komentorivin edistynyt käyttö edellyttää.

Tässä artikkelissa on esitetty johdatus säännöllisiin lausekkeisiin siten, miten komentorivityöklu grep ne ymmärtää laajennettussa (engl. extended) tilassa. Kaikissa säännöllisten lausekkeiden toteutuksissa syntaksi ei kuitenkaan ole aivan sama. Perusoperaattorit (*, +, ?, sulut) ovat kuitenkin universaaleja, eli kaikki toteutukset tukevat niitä.

Säännöllisistä lausekkeista on yleisesti käytössä kolme tyyppiä: tavallinen (engl. basic), laajennettu (engl. extended) ja Perl-tyyppinen. Varsinkin useassa järjestelmässä käytettäviä skriptejä kirjoitettaessa pitää olla huolellinen, mikäli käyttää tavallisesta poikkeavia ilmaisuja. Tässä ohjeessa esitetty syntaksi on yhteensopiva Linuxin GNU-grepin laajennetun syntaksin kanssa jollei toisin mainita.

Säännöllisiä lausekkeita ei pidä sekoittaa tiedostonimi-jokereihin (glob), joilla on samankaltainen toiminnallisuus (vrt. * ja .*).

Teoriaa

Tietojenkäsittelytieteessä puhutaan säännöllisestä kielestä, joka voidaan tunnistaa äärellisellä automaatilla. Säännöllisten lauskkeiden historia juontaa juurensa siten 50- ja 60-luvuille tietojenkäsittelytieteen syntyaikohiin. Unix-järjestelmissä säännöllisiä lausekkeita on voinut hyödyntää aina ensimmäisistä versioista lähtien. Ensimmäinen grep-ohjelma kirjoitettiin ilmeisesti vuonna 1973.[1]

Säännöllisiä lausekkeita voi hyödyntää monissa ohjelmointikielissä (mm. Perl, Java, Python, ECMAScript). Mille tahansa Turing-täydelliselle ohjelmointikielelle voidaan myös aina kirjoittaa säännöllisten lausekkeiden tulkki.

Johdatus säännöllisiin lausekkeisiin

Perusteet

Säännöllinen lauseke joko vastaa tai ei vastaa merkkijonoa. Kutsutaan säännöllistä lauseketta lausekkeeksi ja merkkijonoa, jonka vastaavuus lausekkeeseen halutaan selvittää, kohteeksi.

Lausekkeen yksinkertaiset kirjoitusmerkit vastaavat yksi-yhteen kohteen kirjoitusmerkkejä. Tämän vuoksi lauseke

abba

vastaa yllätyksettömästi kohdetta

abba

mutta ei kohdetta

bono

Yleensä (mm. grep ja awk) katsotaan, että kohteen alussa ja lopussa voi olla mielivaltainen määrä ei-vastaavia merkkejä. Tällöin kohde (s.o. näiden komentojen tapauksessa rivi) vastaa lauseketta, jos edes jokin sen osa vastaa lauseketta. Siis grep ja awk -yhteydessä lauseke

abba

vastaa kohdetta

boabbabo

ja

dadaabbagada

mutta ei silti kohdetta

babbbab

Operaattorit

Yksi-yhteen vastaavien kirjoitusmerkkien lisäksi säännöllisissä lausekkeissa on mahdollisuus käyttää erilaisia operaattorimerkkejä, joilla on lausekkeessa erikoismerkitys.

Vaihtoehtoisuusoperaattori: |

Näistä ensimmäinen on operaattori |, joka tarkoittaa vaihtelua tai alternointia (engl. alternation). Sen merkitys on sama kuin luonnollisen kielen sanalla "tai". Lauseke

omena|luumu

vastaa sekä kohteita

omena

että

luumu

Kun lisäksi muistamme yltä, että komentorivityökalujen tapauksessa kohde voi sisältää ei-vastaavia merkkejä sekä alussa että lopussa, vastaa ylläoleva lauseke omena|luumu myös merkkijonoja

uuniomena

tai

luumusoppa

Huomaa kuitenkin, että seuraavassa kohteessa vain ensimmäinen esiintymä vastaa lauseketta

kotimainen omena-luumuhillo

Toisto-operaattorit

Merkeillä *, +, ? ja ilmaisulla {n,m} on säännöllisissä lausekkeissa erikoismerkitys, joka vaikuttaa niitä välittömästi edeltävään merkkiin.

Mielivaltaisen monta: *

Operaattori * tarkoittaa "edellinen toistettuna mielivaltaisen monta kertaa, tai ei kertaakaan". Siten lauseke

ab*a

vastaa kaikkia seuraavia kohteita:

abbbbbbbbbbbbbbbbba
abbba
abba
aba
aa

mutta ei seuraavia:

acccca
bba

Todettakooon, että selvästi b-merkkien määrä kohteessa voi olla mielivaltainen, joten eri vastaavia kohdemerkkijonoja on olemassa myös ääretön määrä.

Vähintään yksi: +

Operaattori + tarkoittaa "edellinen toistettuna mielivaltaisen monta kertaa, mutta vähintään kerran". Siten lauseke

ab+a

vastaa seuraavia kohteita:

abbbbbbbbbbbbbbbbba
abbba
abba
aba

mutta ei seuraavia:

aa
acccca
bba
Ehdollinen: ?

Operaattori ? tarkoittaa "edellinen kerran, tai ei kertaakaan". Siten lauseke

ab?a

vastaa seuraavia kohteita:

aba
aa

mutta ei mitään seuraavista:

abbbbbbbbbbbbbbbbba
abbba
abba
acccca
bba
n:stä m:n kertaan: {n, m}

Aina yllä olevat operaattorit *, + ja ? eivät riitä, vaan jotain merkkiä tarvitsee vastata juuri tietyn määrän toistoja. Voitaisiin toki kirjoittaa

aaaa?a?

jos halutaan vastata

aaa
aaaa
aaaaa

mutta voidaan kirjoittaa lyhyemmin

a{3,5}

Tämä tarkoittaa, että edeltävää merkkiä a voi esiintyä kolmesta viiteen kertaan. Käytetään yleisesti tästä ilmaisusta mallia {n,m}, jossa n ja m ovat mielivaltaisia kokonaislukuja, tarkoittaen "n:stä m:n kertaan".

Kumpi tahansa numeroista n ja m voidaan jättää pois. Täten ilmaisu

{n}

tarkoittaa "tasan n kertaa"'. Ilmaisu

{n,}

tarkoittaa "vähintään n kertaa". Ilmaisu

{,m}

tarkoittaa "korkeintaan m kertaa".

Sivuhuomatuksena todettakoon, että yllä olevan perusteella lyhyemmät operaattorit *, + ja ? voitaisiin aina korvata ilmaisuilla {0,}, {1,} ja {0,1} vastaavasti. {m,n}-muoto on kuitenkin uudempi laajennos, joka ei toimi kaikissa ohjelmassa.

Huom! Operaattori {n,m} ei ole tuettu kaikissa säännöllisten lausekkeiden toteutuksissa. GNU grep tukee niitä laajennetussa tilassa (egrep).

Huomautuksia

Operaattoreita voi yhdistää mielivaltaisesti toisiinsa. Siten lauseke

hur+a*!*

vastaavaa kaikkia seuraavia kohteita:

hur
hurr
hurra
hurraa
hurraa!
hurrraaa!!!!

jne.

Toisaalta lauseke ei vastaa mitään seuraavista kohteista:

burraa!
huaa!!
huuuurraa!!
hurrurrur!!

jne.

Tässä vaiheessa on hyvä varmistaa, että ymmärtää edellä esitetyn esimerkin kokonaisuudessaan.

Sulut: ( ja )

Kysymys: Jos halutaan vastata kohdetta hUrrUrrUrr siten, että merkkijono Urr voi esiintyä kohteessa äärettömän monta kertaa, miten tulee toimia?

Vastaus: Tämä on mahdotonta tähän mennessä esitetyillä operaattoreilla. Voisimme toki kirjoittaa hU+r+r+ tai vastaavaa, mutta tämä vastaisi vain kohteita kuten hUUrrrr. Tarvitsemme selvästi uuden operaattorin; sulut.

Merkeillä ( ja ) voidaan määrittää lausekkeen osia, jotka samaan tapaan kuin koulumatematiikassa lasketaan ennen laskun muita osia. Koska säännöllisissä lausekkeissa ei kuitenkaan lasketa mitään, havainnollistetaan sulkujen merkitystä esittämällä ratkaisu yllä olevaan kysymykseen.

Lauseke

h(Urr)+

vastaa kohteita

hUrr
hUrrUrr
hUrrUrrUrr
hUrrUrrUrrUrr

jne. jne. Lauseke ei vastaa seuraavia kohteita:

hArr
hErr
burr

Samoin kohde

hUr

näyttää oikealta, mutta jää kuitenkin liian lyhyeksi, sillä sulkujen sisältö on löydyttävä kohteesta aina kokonaan. Siksi kohde hUr ei vastaa lauseketta h(Urr)+.

Esimerkkejä

Sulkuja voi yhdistellä mielivaltaisesti kaikkiin operaattoreihin *, +, ? ja |. Seuraavassa on annettu sarja lausekkeita, sekä esimerkkejä kutakin vastaavista ja vastaamattomista kohteista:

Lauseke

d(ii)?pada+(pa|ba)?

vastaa esimerkiksi seuraavia kohteita

diipadaaba
diipadaapa
dpadaapa
dpadaaba
diipadaaaaapa
diipadaaaaaba
diipada
diipada
diipadaaa
diipadaaaa

mutta ei mitään seuraavista

dipadaapa
diipaduu
diipad

Viimeinen esimerkki jää lyhyeksi, sillä merkki a on lausekkeessa pakollinen vähintään kerran.

Lauseke

(hi|ha|ho)+

vastaa mitä tahansa kohdetta, jossa esiintyy mikä tahansa määrä merkkijonoja hi, ha ja ho peräkkäin mielivaltaisen monta kertaa, kuitenkin vähintään kerran. Lauseke vastaa siis kaikkia seuraavia:

hi
ha
ho
hihi
haha
hiha
hoho
hihaho
hahohihohahi

mutta ei

he
hu
huhu
hehe

Yhtä merkkiä vastaavat erikoismerkit

Säännöllisissä lausekkeissa voi esiintyä myös joitain erikoismerkkejä, jotka eivät ole operaattoreita, eli ne eivät vaikuta niitä edeltäviin sulkuihin tai merkkeihin. Ne voitaisiin aina kirjoittaa lausekkeilla muotoa

(merkki1|merkki2|merkki3|merkki4|...|merkkiN)

mutta tämä on varsin työlästä ja epäselkeää. Siksi säännöllisissä lausekkeissa on joitain erikoismerkkejä, jotka ovat oikopolkuja monimutkaisten vaihtoehtoisuuslausekkeiden kirjoittamiseen.

Jokerimerkki: .

Merkillä . (piste) on erikoismerkitys. Se vastaa aina mitä tahansa merkkiä. Lauseke

.*

vastaa siis kaikkia mahdollisia kohteita - jopa tyhjää merkkijonoa, sillä operaattori * tarkoittaa, että erikoisoperaattori "mikä tahansa merkki" voi esiintyä myös nolla kertaa.

Lauseke

(h.)+

vastaa siten kaikkia seuraavista:

ha
he
hi
hy
hr
hihrhy

mutta ei

rh
ah
oh
ih
h

Viimeinen esimerkki jää jälleen lyhyeksi, sillä . ei vastaa kuitenkaan "tyhjää" merkkiä.

Lauseke

.upu

vastaa

tupu
hupu
lupu
Tupu
Hupu
Lupu

mutta myös

supu

mutta ei

sepe

Merkkiluokat: []

Jos halutaan korjata ylläoleva Ankanpoika-esimerkki, voitaisiin kirjoittaa:

(t|h|l|T|H|L)upu

joka vastaisi vain ja ainoastaan:

tupu
hupu
lupu
Tupu
Hupu
Lupu

Merkkien [] avulla voidaan määritellä kirjainluokkia siten, että seuraava merkki kohteessa vastaa vain ja ainoastaan jotain hakasulkujen välissä olevaa kirjainta. Siten äskeistä täysin vastaa lauseke on

[thlTHL]upu

Hakasulkujen välissä voidaan myös määritellä lokaalin aakkoston mukaisia välejä väliviivalla -. Tällöin

19[4-9][0-9]|20[0-9][0-9]

vastaa kaikkia vuosilukuja välillä 1940-2099. Samoin

Osasto [A-I]

vastaa

Osasto A
Osasto B
Osasto C
...
Osasto H
Osasto I

mutta ei

Osasto J

On huomioitavaa, että myös kohteet ”Osasto b” tai ”Osasto È” saattavat vastata yllä esitettyä lausketta joissain lokaaleissa.

Jos merkki - halutaan sisällyttää hakasulkuilmaisuun, se jätetään viimeiseksi. Esim.

Ala[ -]aste

vastaa

Ala aste
Ala-aste

Jos oikea hakasulku (]) halutaan sisällyttää hakasulkuilmaukseen, se laitetaan hakasulkujen ensimmäiseksi merkiksi. Esim.

[]abcdef]+

vastaa

a
b
]
ab]
d]f

jne., mutta ei

[gi
zo

Hakasulkuilmaisusn merkitys voidaan kääntää asettamalla hattu ^ sen ensimmäiseksi merkiksi. Tällöin hakasulkuilmaisu vastaa mitä tahansa hakasuluissa ei esiintyvää merkkiä kohteessa. Täten lauseke

[^aeiouyåäö]+

vastaa mitä tahansa pelkistä konsonanteista, välimerkeistä ja numeroista koostuvaa kohdetta, kuten

prrr
123
hkr
brr-hrr

jne. Mutta ei

au
aiai

jne.

Grepin hyväksymissä säännöllisissä lausekkeissa merkin ^ voi sisällyttää hakasulkuilmaukseen laittamalla sen miksi tahansa muuksi merkiksi, kuin hakasulkujen ensimmäinen merkki. Tämä ei pidä välttämättä paikkaansa kuitenkaan kaikilla säännöllisten lausekkeiden toteutuksilla, vaan hattumerkin eteen on mahdollisesti laitettava pako-operaattori \.

POSIX-merkkiluokat

Joitain erikoismerkkiluokkia on määrätty ennakkoon grepissä. Esimerkiksi

  • [:digit:] vastaa mitä tahansa numeroa
  • [:alpha:] vastaa mitä tahansa kirjainta
  • [:alnum:] vastaa mitä tahansa kirjainta tai numeroa
  • [:space:] vastaa mitä tahansa tyhjää (esim. välilyönti, tabulaattori) merkkiä

Muita tällaisia ovat [:lower:], [:upper:], [:xdigit:], [:blank:], [:punct:], [:print:], [:cntrl:] ja [:graph:].

Merkkiluokat tulee laittaa lisäksi ulompien hakasulkujen [] sisään. Esimerkiksi

[[:upper:]]+

joka vastaa yleensä ilmaisua

[A-Z]+

eli mielivaltainen (vähintään yksi) määrä isoja kirjaimia A-Z.

Huom! Merkkiluokkia käytettäessä on huomioitava, että käytetty lokaali vaikuttaa ratkaisevasti siihen, mitä merkkejä luokkaan sisältyy. Siksi niitä ei voi käyttää tietoturvatarkistuksiin muuta kuin ennalta tiedetyillä lokaaleilla.

Pako-operaattori: \

Jos halutaan vastata jotain merkkiä, joka on määritelty operaattoriksi säännöllisissä lausekkeissa, sitä ei voida kirjoittaa sellaisenaan lausekkeeseen. Jos halutaan vastata tarkalleen jotain seuraavista

Mitä?
Missä?
Milloin?

ei voida kirjoittaa lauseketta

(Mi(tä|ssä|lloin))?

koska kysymysmerkki ? lopussa vain ehdollista koko ulomman sulkuilmaisun, ja koko lauseke vastaisi siis vain joko tyhjää merkkijonoa tai kysymysmerkitöntä kysymyssanaa.

Tarvitaan pako-operaattoria \. Merkin ? tai ylipäänsä minkä tahansa opraattorimerkin erikoismerkityksen voi poistaa asettamalla sen eteen kenoviivan. Tällöin kenoviivaa välittömästi seuraava merkki täytyy esiintyä kohteessa sellaisenaan. Oikea tapa kirjoittaa yllä oleva lauseke olisi siten

(Mi(tä|ssä|lloin))\?

Tämä vastaa vain ja ainoastaan haluttuja merkkijonoja.

Seuraavat operaattorit on merkittävä pako-operaattorilla, jos niitä ei haluta tulkittavan erikoismerkeiksi:

( ) | * + ? { } [ ] ^ $ \

Siis, jos halutaan vastata kohteessa kenoviivaa, on kirjoitettava \\. Esimerkiksi Windows-polku:

<asema>:\Dokumentit\<nimi>.(doc, docx tai odt)

voidaan löytää esim. seuraavalla säännöllisellä lausekkeella:

[A-Z]:\\Dokumentit\\[a-öA-Ö0-9 _-]+\.(doc|docx|odt)

Rivin alku ja loppu: ^ ja $

On voinut herätä kysymys, kuinka voidaan vastata kohdetta (s.o. riviä) tarkalleen. Heti alussa selvisi, että esim grepin mielestä kohde (rivi) vastaa lauseketta, jos lauseke esiintyy missä tahansa kohdassa riviä. Jos halutaan, että lauseke vastaa alusta loppuun tarkalleen koko riviä, on otettava käyttöön erikoismerkit ^ ja $. Nämä vastaavat kohteessa rivin tai merkkijonon alkua ja loppua kuvaavia "näkymättömiä" merkkejä vastaavasti. Siten

^abba$

vastaa vain riviä

abba

mutta ei esimerkiksi

babba
abbab
babbab
aba
abbbba
^abba
abba$
^abba$

Esimerkki

Merkit ^ ja $ antavat viimeisen silauksen grepin tehokkaaseen hyödyntämiseen. Nyt esimerkiksi, jos tulostetaan kaikki prosessit komennolla ps -ef, saadaan yleensä jokseenkin tälläinen listaus (esimerkissä palvelinkone):

$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Jan21 ?        00:00:34 init [2]      
root      1425     1  0 Jan31 ?        00:00:00 /usr/sbin/xinetd
qmails    3986     1  0 Jan21 ?        00:00:00 qmail-send
qmaill    3990  3986  0 Jan21 ?        00:00:00 splogger qmail 2
root      3993  3986  0 Jan21 ?        00:00:00 qmail-lspawn | /usr/bin/deliverquota ./Maildir
qmailr    3994  3986  0 Jan21 ?        00:00:00 qmail-rspawn
qmailq    3995  3986  0 Jan21 ?        00:00:00 qmail-clean
root      5566     1  0 Jan21 ?        00:00:05 /usr/sbin/apache2 -k start
root      6091     1  0 Jan21 ?        00:00:03 /usr/sbin/cron
www-data 15379  5566  0 Feb14 ?        00:00:00 /usr/sbin/apache2 -k start
www-data 15386  5566  0 Feb14 ?        00:00:00 /usr/sbin/apache2 -k start
root     21684 32395  0 20:37 ?        00:00:00 sshd: user [priv]
user     21688 21684  0 20:37 ?        00:00:00 sshd: user@pts/1
user     21689 21688  0 20:37 pts/1    00:00:00 -bash
user     21705 21689  0 20:37 pts/1    00:00:00 ps -ef
syslog   32279     1  0 Jan21 ?        00:00:07 /sbin/syslogd -u syslog
bind     32351     1  0 Jan31 ?        00:00:00 /usr/sbin/named
root     32395     1  0 Jan21 ?        00:00:22 /usr/sbin/sshd
(... jne ...)

Jos haluamme ylläolevasta listasta vain "qmail" -alkuisten käyttäjien prosessit, voisimme kirjoittaa

ps -ef | grep qmail

Tämä kuitenkin tulostaa myös kaikki sellaiset prosessit, joiden nimikentässä esiintyy "qmail" – joukossa myös käyttäjän root prosesseja vastoin alkuperäistä tarkoitusta:

$ ps -ef | grep qmail
qmails    3986     1  0 Jan21 ?        00:00:00 qmail-send
qmaill    3990  3986  0 Jan21 ?        00:00:00 splogger qmail 2
root      3993  3986  0 Jan21 ?        00:00:00 qmail-lspawn | /usr/bin/deliverquota ./Maildir
qmailr    3994  3986  0 Jan21 ?        00:00:00 qmail-rspawn
qmailq    3995  3986  0 Jan21 ?        00:00:00 qmail-clean

Ratkaisu qmail -alkuisten käyttäjien prosessien listaamiseen on:

$ ps -ef | grep ^qmail
qmails    3986     1  0 Jan21 ?        00:00:00 qmail-send
qmaill    3990  3986  0 Jan21 ?        00:00:00 splogger qmail 2
qmailr    3994  3986  0 Jan21 ?        00:00:00 qmail-rspawn
qmailq    3995  3986  0 Jan21 ?        00:00:00 qmail-clean

Hattumerkki ^ alussa vastaa jokaisen listausrivin alkua, jota pitää välittömästi seurata merkkijono qmail, ja tämän jälkeen voi tulla mikä tahansa merkkejä, koska loppumerkkiä ei ole tarkoituksella annettu. Grep tulostaa vain lausketta vastaavat rivit, ja tulos on haluttu.

Pako komentotulkista

Jos halutaan antaa jokin monimutkaisempi ilmaisu, on hyvä asettaa säännöllinen lauseke yksinkertaisten heittomerkkien ' sisään, jotta ne regexp-operaattorit, joillla on jokin erikoismerkitys komeotulkissa eivät aiheuta ongelmia.

$ echo moi | egrep m(o|a)i
-bash: syntax error near unexpected token `('

Sululla ( on erikoismerkitys komentotulkissa, samoin merkillä |. Ratkaisu:

$ echo moi | egrep 'm(o|a)i'
moi
Laajennetut säännölliset lausekkeet: egrep

Esimerkissä käytettiin komentoa egrep. Se on oikopolku grepin valitsimelle -E, joka ottaa säännöllisten lausekkeiden laajennetun (engl. extended) tuen käyttöön. Normaalissa käytössä (pelkkä komento grep) operaattorit ? + {} | ( ja ) eivät ole käytettävissä, paitsi asettamalla niiden eteen pako-operaattorin \.

Prosessoitu tuloste: awk

Jos ylläolevassa prosessilistausesimerkissä halutaan tulostaa pelkät prosessien PID-numerot, voidaan käyttää awk-työkalua seuraavasti:

$ ps -ef | awk '/^qmail/ { print $2 }'
3986
3990
3994
3995

Esimerkkejä lausekkeista

Web-kuva:

[^ ]+\.(png|PNG|jpg|JPG|jpeg|JPEG|gif|GIF)

Päivämäärä muotoa pp.kk.vvvv:

[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{4}

2-merkkinen heksadesimaali:

[0-9A-Fa-f]{2}

Sähköpostiosoite:

[a-zA-Z0-9_]+@[a-zA-Z0-9_\.]+\.[a-zA-Z]{2,3}

Laajennukset

Lukuunottamatta operaattoria {n,m} ja POSIX-luokkia ([[:luokka:]] jne.) yllä esitetty on pitkälti universaalisti tuettua säännöllisten lausekkeiden eri toteutuksissa. Monissa toteutuksissa on kuitenkin tehty laajennuksia tähän, mutta esim. grep ei tue seuraavia.

Pakoluokat

Jos halutaan kirjoittaa lauseke, joka jossain kohtaa vastaa mitä tahansa numeromerkkiä, voidaan kirjoittaa hakasulkuilmaus [0-9]. Tälläisille yleisesti käytetyille luokille on kuitenkin olemassa laajennuksissa helpompia nimiä. Kutsutaan näitä pakoluokiksi. Ne muistuttavat läheisesti POSIX-merkkiluokkia, mutta ovat lyhyempiä.

Pakoluokat
Ilmaisu Merkitys Vastaava hakasulkuilmaus
\w Mikä tahansa aakkonen a-z, numero tai alaviiva _. [a-zA-Z0-9_]
\W Äskeisen vastakohta; mikä tahansa muu kuin aakkonen a-z, numero tai alaviiva. [^a-zA-Z0-9_]
\s Mikä tahansa tyhjä merkki (esim. välilyönti tai tabulaattori) esim. [ \t], jossa \t tabulaattorimerkki
\S Äskeisen vastakohta; mikä tahansa ei-tyhjä merkki esim. [^ \t]
\d Mikä tahansa numero. [0-9]
\D Äskeisen vastakohta; mikä tahansa ei-numero. [^0-9]

Lauseke

\d+

vastaa siis kaikkia numeroita aivan siinä missä myös

[0-9]+

Katso myös

Aiheesta muualla