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

Siirry navigaatioon Siirry hakuun
22 689 merkkiä lisätty ,  9. syyskuuta 2020
Selvennetty muutamia lyhenteitä.
p (→‎Wikipedia: linkki)
(Selvennetty muutamia lyhenteitä.)
 
(24 välissä olevaa versiota 6 käyttäjän tekeminä ei näytetä)
Rivi 1: Rivi 1:
Säännöllinen lauseke (engl. regular expression, regexp) on merkkijonotemplaatti [[wikipedia:fi:säännöllinen kieli| säännölliselle kielelle]], joka siis voidaan tunnistaa [[wikipedia:fi:äärellinen automaatti|äärellisellä automaatilla]]. Säännöllisiä lausekkeita käytetään monissa ohjelmointikielissä.
'''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ä lausekkeista 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ää.


==Toteutukset==
Tässä artikkelissa on esitetty johdatus säännöllisiin lausekkeisiin siten, miten komentorivityökalu <tt>[[grep]]</tt> ne ymmärtää ''laajennetussa'' (engl. extended) tilassa. Perusoperaattorit (<tt>*</tt>, <tt>?</tt>, sulut) ovat ''universaaleja'', eli kaikki toteutukset tukevat niitä. Kaikissa säännöllisten lausekkeiden toteutuksissa syntaksi ei kuitenkaan ole aivan sama.  
===[[Posix]]-tyylinen===
====elementit====
*[<merkkejä>] mikä tahansa merkeistä, tässä usein sallitaan myös laajempia ilmauksia kuten a-z, englantilaisille pienaakkosille, ja 0-9, numeroille.
*\(<sisältö elementtinä>\) merkitsee sisältönsä yhdeksi elementiksi toistolauseille ulospäin täydelliseksi säännölliseksi lausekkeeksi sisällepäin.
====toisto-operaattorit====
*'*': edellinen elementti 0 tai mielivaltaisen montaa kertaa kokonaan
*'+': edellinen elementti mielivaltaisen monta kertaa
*'?': edellinen elementti 0 tai 1 kertaa
monissa sovelluksissa sallitaan myös
*'{<toistokerrat>}': edellinen elementti toistokerrat kertaa
====unioni(tai)====
merkkijono kuuluu kieleen, jos se kuuluu | edeltävään tai sen jälkeiseen säännölliseen lauseekkeeseen.


====erityismerkit====
Säännöllisistä lausekkeista on yleisesti käytössä kolme tyyppiä: ''tavallinen'' (engl. basic), ''laajennettu ''(engl. extended) ja ''[[Perl]]-tyyppinen''. Tässä ohjeessa esitetty syntaksi on yhteensopiva Linuxin [[GNU]]-grepin laajennetun syntaksin kanssa jollei toisin mainita. GNU grepin syntaksi taas on yhteensopiva [[POSIX]]-standardin mukaisten säännöllisten lausekkeiden kanssa. Varsinkin useassa järjestelmässä käytettäviä [[skripti|skriptejä]] kirjoitettaessa täytyy olla huolellinen siinä, mitkä rakenteet ovat uudempia laajennoksia. Erotuksena grep myös käsittelee syötettä rivi riviltä. Toisissa ohjelmissa saatetaan käsitellä rivinvaihtoa tavallisena merkkinä.
*'^': Rivin alku, tämä ei siis vastaa rivin ensimmäistä merkkiä vaan vain rivin alkua.
 
*'$': Rivin loppu, vastaavasti.
Säännöllisiä lausekkeita ei tule sekoittaa tiedostonimi-jokereihin (glob), joilla on samantapainen toiminnallisuus (vrt. <tt>*</tt> ja <tt>.*</tt>). Tiedostonimijokereita ja säännöllisiä lausekkeita voi hyödyntää usein samoissa yhteyksissä, mutta säännölliset lausekkeet ovat tiedostonimijokereita ilmaisuvoimaisempia.
*'.': mikä tahansa merkki
 
===[[Perl]]===
== Teoriaa ==
[[wikipedia:fi:Tietojenkäsittelytiede|Tietojenkäsittelytieteessä]] säännöllisiä lausekkeita käsitellään [[wikipedia:fi:säännöllinen kieli|säännöllisten kielten]] teoriassa. Säännöllinen lauseke on esimerkki [[wikipedia:fi:äärellinen automaatti|äärellisestä automaatista]]. Säännöllisten lausekkeiden historia juontaa juurensa 50- ja 60-luvuille tietojenkäsittelytieteen syntyaikoihin.
 
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]].
 
== Historia ==
Ensimmäisenä säännöllisten lausekkeiden toteutuksena aidolle tietokoneelle (IBM 7094) pidetään Unix-pioneeri [[wikipedia:fi:Ken Thompson|Ken Thompson]]in julkaisua [http://portal.acm.org/citation.cfm?doid=363347.363387 Regular Expression Search Algorithm] vuodelta 1968. [[Unix]]-järjestelmissä säännöllisiä lausekkeita on siten 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>
 
== 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 (eli näiden komentojen tapauksessa jokin ''rivi'' jossain tiedostossa) vastaa lauseketta, jos edes jokin sen ''osa'' vastaa lauseketta. Siis grep ja awk -yhteydessä lauseke
abba
vastaa kohdetta
bo'''abba'''bo
ja
dada'''abba'''gada
mutta ei silti kohdetta
b'''abb'''bab
 
===Operaattorit===
Yksi-yhteen vastaavien kirjoitusmerkkien lisäksi säännöllisissä lausekkeissa on mahdollisuus käyttää erilaisia ''operaattorimerkkejä'', joilla on lausekkeessa erikoismerkitys.
 
====Vaihtoehtoisuusoperaattori: <tt>|</tt>====
Näistä ensimmäinen on operaattori <tt>|</tt>, 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 <tt>omena|luumu</tt> myös merkkijonoja
uuni'''omena'''
tai
'''luumu'''soppa
Huomaa kuitenkin, että seuraavassa kohteessa vain ensimmäinen esiintymä vastaa lauseketta
kotimainen '''omena'''-luumuhillo
 
====Toisto-operaattorit====
Merkeillä <tt>*</tt>, <tt>+</tt>, <tt>?</tt> ja ilmaisulla <tt>{n,m}</tt> on säännöllisissä lausekkeissa erikoismerkitys, joka vaikuttaa niitä ''välittömästi edeltävään'' merkkiin.
 
=====Mielivaltaisen monta: <tt>*</tt>=====
Operaattori <tt>*</tt> 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:
'''a'''cccca
bba
 
Todettakoon, 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: <tt>+</tt>=====
Operaattori <tt>+</tt> 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:
'''a'''a
'''a'''cccca
bba
 
Lausekkeen x+ voi kirjoittaa myös xx*, jolloin <tt>+</tt>-operaattoria ei tarvitse käyttää (se ei sisälly kaikkiin "tavallisen" syntaksin toteutuksiin).
 
=====Ehdollinen: <tt>?</tt>=====
Operaattori <tt>?</tt> tarkoittaa ''"edellinen kerran, tai ei kertaakaan''". Siten lauseke
ab?a
vastaa seuraavia kohteita:
'''aba'''
'''aa'''
mutta ei mitään seuraavista:
'''ab'''bbbbbbbbbbbbbbbba
'''ab'''bba
'''ab'''ba
'''a'''cccca
bba
 
=====n:stä m:n kertaan: <tt>{n, m}</tt>=====
Aina yllä olevat operaattorit <tt>*</tt>, <tt>+</tt> ja <tt>?</tt> 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ä <tt>a</tt> voi esiintyä kolmesta viiteen kertaan. Käytetään yleisesti tästä ilmaisusta mallia <tt>{n,m}</tt>, jossa <tt>n</tt> ja <tt>m</tt> ovat mielivaltaisia kokonaislukuja, tarkoittaen ''"n:stä m:n kertaan"''.
 
Kumpi tahansa numeroista <tt>n</tt> ja <tt>m</tt> 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"''.
 
Sivuhuomautuksena 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 =====
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!
'''hu'''aa!!
'''hu'''uuurraa!!
'''hurr'''urrur!! // miksi ei vastaa???
jne.
 
Tässä vaiheessa on hyvä varmistaa, että ymmärtää edellä esitetyn esimerkin kokonaisuudessaan.
 
===Sulut: <tt>(</tt> ja <tt>)</tt>===
'''Kysymys:''' Jos halutaan vastata kohdetta <tt>hUrrUrrUrr</tt> siten, että merkkijono <tt>Urr</tt> 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 <tt>hU+r+r+</tt> tai vastaavaa, mutta tämä vastaisi vain kohteita kuten <tt>hUUrrrr</tt>. Tarvitsemme selvästi uuden operaattorin; ''sulut''.
 
Merkeillä <tt>(</tt> ja <tt>)</tt> 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:
'''h'''Arr
'''h'''Err
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 <tt>hUr</tt> <u>ei</u> vastaa lauseketta <tt>h(Urr)+</tt>.
 
====Esimerkkejä====
 
Sulkuja voi yhdistellä mielivaltaisesti kaikkiin operaattoreihin <tt>*</tt>, <tt>+</tt>, <tt>?</tt> ja <tt>|</tt>. 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
'''di'''padaapa
'''diipad'''uu
'''diipad'''
 
Viimeinen esimerkki jää lyhyeksi, sillä merkki <tt>a</tt> on lausekkeessa pakollinen vähintään kerran.
 
Lauseke
(hi|ha|ho)+
vastaa mitä tahansa kohdetta, jossa esiintyy mikä tahansa määrä merkkijonoja <tt>hi</tt>, <tt>ha</tt> ja <tt>ho</tt> 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
'''h'''e
'''h'''u
'''h'''uhu
'''h'''ehe
 
===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: <tt>.</tt>====
Merkillä <tt>.</tt> (piste) on erikoismerkitys. Se vastaa aina ''mitä tahansa'' merkkiä. Lauseke
.*
vastaa siis kaikkia mahdollisia kohteita - jopa tyhjää merkkijonoa, sillä operaattori <tt>*</tt> 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ä <tt>.</tt> ei vastaa kuitenkaan "tyhjää" merkkiä.
 
Lauseke
.upu
vastaa
'''tupu'''
'''hupu'''
'''lupu'''
'''Tupu'''
'''Hupu'''
'''Lupu'''
mutta myös
'''supu'''
mutta ei
sepe
 
====Merkkiluokat: <tt>[]</tt>====
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 <tt>[]</tt> 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ä [[lokaali]]n aakkosjärjestyksen (LC_COLLATE) mukaisia välejä väliviivalla <tt>-</tt>. 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ä lauseketta joissain [[lokaali|lokaaleissa]].
 
Jos merkki <tt>-</tt> halutaan sisällyttää hakasulkuilmaisuun, se jätetään viimeiseksi. Esim.
Ala[ -]aste
vastaa
'''Ala aste'''
'''Ala-aste'''
 
Jos oikea hakasulku (<tt>]</tt>) halutaan sisällyttää hakasulkuilmaukseen, se laitetaan hakasulkujen ensimmäiseksi merkiksi. Esim.
[]abcdef]+
vastaa
'''a'''
'''b'''
''']'''
'''ab]'''
'''d]f'''
jne., mutta ei
[gi
zo
 
Hakasulkuilmaisun merkitys voidaan kääntää asettamalla hattu <tt>^</tt> sen ensimmäiseksi merkiksi. Tällöin hakasulkuilmaisu vastaa mitä tahansa hakasuluissa <u>'''ei'''</u> 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 <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 C-lokaalilla (LC_CTYPE=C) 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>====
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 <tt>?</tt> lopussa vain ehdollista koko ulomman sulkuilmaisun, ja koko lauseke vastaisi siis vain joko tyhjää merkkijonoa tai kysymysmerkitöntä kysymyssanaa.
 
Tarvitaan pako-operaattoria <tt>\</tt>. Merkin <tt>?</tt> 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 <tt>\\</tt>. 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: <tt>^</tt> ja <tt>$</tt>===
On voinut herätä kysymys, kuinka voidaan vastata kohdetta (eli 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 <tt>^</tt> ja <tt>$</tt>. 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
'''ab'''a
'''abb'''bba
^abba
'''abba'''$
^abba$
 
==== Esimerkki ====
 
Merkit <tt>^</tt> ja <tt>$</tt> antavat viimeisen silauksen grepin tehokkaaseen hyödyntämiseen. Nyt esimerkiksi, jos tulostetaan kaikki [[prosessi]]t komennolla <tt>[[ps]] -ef</tt>, 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äjä|käyttäjien]] prosessit, voisimme [[putki]]ttaa syötteen grepille kirjoittamalla
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
'''qmail'''s    3986    1  0 Jan21 ?        00:00:00 qmail-send
'''qmail'''l    3990  3986  0 Jan21 ?        00:00:00 splogger qmail 2
root      3993  3986  0 Jan21 ?        00:00:00 '''qmail'''-lspawn | /usr/bin/deliverquota ./Maildir
'''qmail'''r    3994  3986  0 Jan21 ?        00:00:00 qmail-rspawn
'''qmail'''q    3995  3986  0 Jan21 ?        00:00:00 qmail-clean
 
Ratkaisu qmail -alkuisten käyttäjien prosessien listaamiseen on:
$ ps -ef | grep ^qmail
'''qmail'''s    3986    1  0 Jan21 ?        00:00:00 qmail-send
'''qmail'''l    3990  3986  0 Jan21 ?        00:00:00 splogger qmail 2
'''qmail'''r    3994  3986  0 Jan21 ?        00:00:00 qmail-rspawn
'''qmail'''q    3995  3986  0 Jan21 ?        00:00:00 qmail-clean
 
Hattumerkki <tt>^</tt> alussa vastaa jokaisen listausrivin alkua, jota pitää välittömästi seurata merkkijono <tt>qmail</tt>, 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 <tt>'</tt> sisään, jotta ne regexp-operaattorit, joillla on jokin erikoismerkitys [[komentotulkki|komentotulkissa]] eivät aiheuta ongelmia.
$ echo moi | egrep m(o|a)i
-bash: syntax error near unexpected token `('
Sululla <tt>(</tt> on erikoismerkitys komentotulkissa, samoin merkillä <tt>[[putki||]]</tt>. Ratkaisu:
$ echo moi | egrep 'm(o|a)i'
moi
 
===== 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>. Operaattoriella <tt>* . [] ^</tt> ja <tt>$</tt> on sama erikoismerkitys sekä käytettäessä normaalia (<tt>grep</tt>) että laajennettua (<tt>egrep</tt>) syntaksia, mutta esimerkiksi merkinnöillä <tt>\(</tt> ja <tt>(</tt> siis päinvastainen.
 
===== 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 [[wikipedia:fi:heksadesimaalijärjestelmä|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 <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. <tt>[[grep]]</tt> ei tue seuraavia.
 
=== Pakoluokat ===
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"
|+'''Pakoluokat'''
|-
! Ilmaisu !! Merkitys !! Vastaava hakasulkuilmaus
|-
| <tt>\w</tt> || Mikä tahansa aakkonen a-z, numero tai alaviiva <tt>_</tt>. || <tt>[a-zA-Z0-9_]</tt>
|-
| <tt>\W</tt> || Äskeisen vastakohta; mikä tahansa muu kuin aakkonen a-z, numero tai alaviiva. || <tt>[^a-zA-Z0-9_]</tt>
|-
| <tt>\s</tt> || Mikä tahansa tyhjä merkki (esim. välilyönti tai tabulaattori) || esim. <tt>[ \t]</tt>, jossa <tt>\t</tt> tabulaattorimerkki
|-
| <tt>\S</tt> || Äskeisen vastakohta; mikä tahansa ei-tyhjä merkki || esim. <tt>[^ \t]</tt>
|-
| <tt>\d</tt> || Mikä tahansa numero. || <tt>[0-9]</tt>
|-
| <tt>\D</tt> || Äskeisen vastakohta; mikä tahansa ei-numero. || <tt>[^0-9]</tt>
|}
 
Lauseke
\d+
vastaa siis kaikkia numeroita aivan siinä missä myös
[0-9]+
 
==Katso myös==
* [[Komentorivin perusteet]]
* [[Grep]] ja [[Awk]]
** [[Putki]]
* [[Ed]] ja [[Sed]]: tuki säännöllisille lausekkeille
* [[Vi]] ja [[vim]]: näiden graaffinen tila
* [[Emacs]]


==Aiheesta muualla==
==Aiheesta muualla==
===Wikipedia===
*[[wikipedia:fi:säännöllinen lauseke|Säännöllinen lauseke]]
*[[wikipedia:fi:säännöllinen lauseke|Säännöllinen lauseke]]
*[[wikipedia:fi:äärellinen automaatti|Äärellinen automaatti]]
*[[wikipedia:fi:äärellinen automaatti|Äärellinen automaatti]]
*[[wikipedia:fi:pinoautomaatti|Pinoautomaatti]]
*[[wikipedia:fi:pinoautomaatti|Pinoautomaatti]]
*[[wikipedia:fi:turingin kone|Turingin kone]]
*[[wikipedia:fi:turingin kone|Turingin kone]]
*[http://www.ohjelmointiputka.net/opas.php?tunnus=phpsl|Säännölliset lausekkeet PHP:ssä] -opas Ohjelmointiputkassa
*[http://www.ohjelmointiputka.net/oppaat/opas.php?tunnus=php_16 Säännölliset lausekkeet PHP:ssä] -opas Ohjelmointiputkassa
*[http://swtch.com/~rsc/regexp/regexp1.html Regular Expression Matching Can Be Simple And Fast]: Keskustelua säännöllisten lausekkeiden toteutuksesta C-kielellä englanniksi.


[[Luokka:Käsitteet]]
[[Luokka:Käsitteet]]
[[Luokka:Komentorivi]]
[[Luokka:Perustietoa]]
Rekisteröitymätön käyttäjä

Navigointivalikko