Kirjoittaja: Antti Laaksonen (2011).
Säännöllinen lauseke on kuvaus merkkijonon rakenteesta. Esimerkiksi säännöllinen lauseke a(b|c)d*
vaatii, että merkkijono alkaa merkillä a
, sitten tulee merkki b
tai c
ja lopuksi on mikä tahansa määrä merkkiä d
. Esimerkiksi merkkijonot abdd
ja acddddd
vastaavat tätä lauseketta.
Ohjelmoinnissa säännöllisistä lausekkeista on hyötyä esimerkiksi silloin, kun tehtävänä on tarkistaa, onko annettu merkkijono oikeanmuotoinen. Usein yhdellä säännöllisellä lausekkeella voi toteuttaa tarkistuksen, johon muuten vaadittaisiin monimutkainen koodinpätkä.
Tässä oppaassa tutustumme säännöllisiin lausekkeisiin PHP:ssä. Oppaan tietoja voi soveltaa myös muissa ohjelmointikielissä, koska säännöllisten lausekkeiden perusasiat ovat samanlaisia eri kielissä.
Oletetaan, että tehtävänä on tunnistaa sanat, jotka muodostuvat kirjaimista a–z ja joiden pituus on 4–10 kirjainta. Tunnistuksen voi toteuttaa seuraavasti tähän mennessä opituin keinoin:
<?php $sana = "selleri"; $kirjaimet = "abcdefghijklmnopqrstuvwxyz"; $kelvollinen = true; for ($i = 0; $i < strlen($sana); $i++) { if (substr_count($kirjaimet, $sana[$i]) == 0) { $kelvollinen = false; break; } } if (strlen($sana) < 4 || strlen($sana) > 10) { $kelvollinen = false; } if ($kelvollinen) { echo "Sana on kelvollinen."; } else { echo "Sana ei ole kelvollinen."; } ?>
Säännöllisen lausekkeen avulla koodista tulee näin lyhyt:
<?php $sana = "selleri"; if (preg_match("/^[a-z]{4,10}$/", $sana)) { echo "Sana on kelvollinen."; } else { echo "Sana ei ole kelvollinen."; } ?>
Seuraavaksi katsomme tarkemmin, miten säännölliset lausekkeet muodostuvat.
Säännöllisen lausekkeen perusosat ovat vaihtoehto, ryhmittely ja toisto.
Vaihtoehto ilmaistaan |
-merkin avulla. Esimerkiksi lauseke apina|banaani|cembalo
vastaa merkkijonoja apina
, banaani
ja cembalo
.
Ryhmittely merkitään sulkujen avulla. Esimerkiksi lauseke (tammi|helmi|maalis)kuu
vastaa merkkijonoja tammikuu
, helmikuu
ja maaliskuu
.
Toistoon ovat käytettävissä seuraavat merkinnät:
merkintä | selitys |
---|---|
* | toisto miten monta kertaa tahansa, myös 0 kertaa |
+ | toisto miten monta kertaa tahansa, ainakin kerran |
? | toisto 0 tai 1 kertaa |
{n} | toisto tarkalleen n kertaa |
{n,} | toisto ainakin n kertaa |
{n,m} | toisto ainakin n , korkeintaan m kertaa |
Esimerkiksi lauseke (ab)+
vastaa merkkijonoja ab
, abab
, ababab
jne.
Merkkiryhmässä hakasulkujen sisällä on joukko merkkejä. Esimerkiksi lauseke [a-zA-Z]
vastaa englannin kielen aakkosia. Jos ensimmäinen merkki on ^
, merkitys muuttuu käänteiseksi: lauseke [^a-zA-Z]
hyväksyy kaikki merkit paitsi englannin kielen aakkoset. Lisäksi merkki .
tarkoittaa mitä tahansa merkkiä.
Huomaa, että kaikki esitellyt merkinnät voisi korvata käyttämällä vain merkkejä |
ja *
sekä sulkuja. Esimerkiksi lauseketta (ab)+
vastaa lauseke ab(ab)*
ja lauseketta [a-f]
vastaa lauseke a|b|c|d|e|f
.
PHP:n tärkeimmät säännöllisiin lausekkeisiin liittyvät funktiot ovat:
preg_match
tunnistaa säännöllisen lausekkeen yhden esiintymän merkkijonossa.preg_match_all
tunnistaa säännöllisen lausekkeen kaikki esiintymät merkkijonossa.preg_replace
korvaa säännöllisen lausekkeen esiintymät merkkijonossa halutulla tavalla.preg_replace_callback
korvaa säännöllisen lausekkeen esiintymät merkkijonossa omalla funktiolla.PHP:ssä säännöllinen lauseke kirjoitetaan merkkien /
sisään. Lisäksi mahdollisten säännöllisten lausekkeiden erikoismerkkien eteen täytyy laittaa merkki \
. Esimerkiksi lauseke "/\([a-z]*\)/"
vastaa merkkijonoja, joissa sulkujen sisällä on mikä tahansa määrä merkkejä a–z.
Tutustumme seuraavaksi PHP:n funktioiden käyttöön esimerkkien kautta.
Seuraava koodi tarkistaa, onko merkkijono viikonpäivän lyhenne:
<?php $paiva = "to"; if (preg_match("/^(ma|ti|ke|to|pe|la|su)$/", $paiva)) { echo "Viikonpäivä on oikeanmuotoinen."; } else { echo "Viikonpäivä ei ole oikeanmuotoinen."; } ?>
Merkki ^
tarkoittaa merkkijonon alkua ja merkki $
tarkoittaa merkkijonon loppua. Ilman näitä merkkejä säännöllinen lauseke tulkitsisi viikonpäiväksi esimerkiksi merkkijonon "aatos", koska sen osana on merkkijono "to".
Seuraava koodi tarkistaa, onko kellonaika oikeanmuotoinen:
<?php $kello = "17:05:22"; $lauseke = "/^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/"; if (preg_match($lauseke, $kello)) { echo "Kellonaika on oikeanmuotoinen."; } else { echo "Kellonaika ei ole oikeanmuotoinen."; } ?>
Säännöllinen lauseke muodostuu kolmesta osasta, joiden välissä on kaksoispiste. Lausekkeen ensimmäinen osa vastaa kaksinumeroista lukua, joka alkaa numerolla 0 tai 1 ja päättyy numeroon 0–9 tai alkaa numerolla 2 ja päättyy numeroon 0–3. Lausekkeen kaksi muuta osaa vastaavat kaksinumeroista lukua, joka alkaa numerolla 0–5 ja päättyy numeroon 0–9.
Seuraava koodi erottaa tekstissä olevat luvut taulukkoon:
<?php $teksti = "Vuodessa on 12 kuukautta ja 52 viikkoa."; preg_match_all("/[0-9]+/", $teksti, $luvut); foreach ($luvut[0] as $luku) { echo $luku . "<br>"; } ?>
Koodin tulostus on seuraava:
12
52
Tässä funktio preg_match_all
kerää taulukkoon $luvut
kaikki merkkijonon $lause
osat, jotka vastaavat säännöllistä lauseketta [0-9]+
eli ovat lukuja.
Seuraava koodi erottaa tekstissä olevat linkit taulukkoon:
<?php $teksti = "Tässä <a href=\"lause.html\">lauseessa</a> on " . "<a href=\"viisi.html\">viisi</a> sanaa."; $lauseke = "/<a href=\"(.*?)\">(.*?)<\/a>/"; preg_match_all($lauseke, $teksti, $linkit); foreach ($linkit[1] as $linkki) { echo $linkki . "<br>"; } ?>
Koodin tulostus on seuraava:
lause.html
viisi.html
Lausekkeessa linkin osoitteen ja otsikon kohdalla on merkintä (.*?)
. Tässä sekä sulkujen että kysymysmerkin merkitys poikkeaa aiemmin esitetystä.
Sulkujen avulla linkin osoite ja otsikko tulevat omiin kohtiinsa taulukossa $linkit
. Taulukon kohdassa 0 ovat lausekkeen kokonaiset esiintymät tekstissä, kohdassa 1 ovat ensimmäisen sulkuosuuden esiintymät ja kohdassa 2 ovat toisen sulkuosuuden esiintymät. Käytännössä taulukon sisältö on seuraava:
taulukon kohta | sisältö |
---|---|
$linkit[0][0] | <a href="lause.html">lauseessa</a> |
$linkit[0][1] | <a href="viisi.html">viisi</a> |
$linkit[1][0] | lause.html |
$linkit[1][1] | viisi.html |
$linkit[2][0] | lauseessa |
$linkit[2][1] | viisi |
Kysymysmerkki määrittää, että lausekkeen osa tunnistaa mahdollisimman lyhyen pätkän tekstiä. Ilman kysymysmerkkiä tunnistettava osa olisi mahdollisimman pitkä, jolloin lauseke erottaisi tekstistä vain yhden "linkin":
lause.html">lauseessa</a> on <a href="viisi.html
Seuraava koodi poistaa tekstistä kaikki muut merkit paitsi numeromerkit:
<?php $teksti = "Vuodessa on 12 kuukautta ja 52 viikkoa."; $teksti = preg_replace("/[^0-9]/", "", $teksti); echo $teksti; ?>
Koodin tulostus on seuraava:
1252
Seuraava koodi korvaa tekstissä olevat tagit [b]...[/b] tageilla <b>...</b>:
<?php $teksti = "Tässä [b]lauseessa[/b] on [b]viisi[/b] sanaa."; $teksti = preg_replace("/\[b\](.*?)\[\/b\]/", "<b>$1</b>", $teksti); echo $teksti; ?>
Koodin tulostus on seuraava:
Tässä lauseessa on viisi sanaa.
Korvaavassa tekstissä $0
viittaa koko säännölliseen lausekkeeseen, $1
viittaa ensimmäiseen sulkuosuuteen, $2
viittaa toiseen sulkuosuuteen jne.
Mutkikkaampia korvauksia voi tehdä niin, että uusi teksti tuotetaankin omalla funktiolla. Tämä mahdollistaa esimerkiksi seuraavan tyylikkään korvauksen:
<?php function jakolasku($m) { // $m = array("6/17", "6", "17") return round($m[1] / $m[2], 3); } $teksti = "Tulokset ovat 6/17 ja 10/21."; $teksti = preg_replace_callback("/([0-9]+)\/([0-9]+)/", "jakolasku", $teksti); echo $teksti; ?>
Koodin tulostus on seuraava:
Tulokset ovat 0.353 ja 0.476.
Lausekkeen ideana on muuttaa tekstissä esiintyvät murtoluvut desimaaliluvuiksi. Funktio jakolasku
saa parametrikseen taulukon, jossa on ensin koko osuma ja sitten erikseen sulkuihin osuneet kohdat eli ensin koko murtoluku ja sitten murtoluvun osoittaja ja nimittäjä. Näistä lasketaan osamäärä, ja funktio round
pyöristää tuloksen kolmen desimaalin tarkkuudelle.
Ohjelmointiputkan SL-haaste sisältää tehtäviä, jotka opettavat säännöllisten lausekkeiden suunnittelua. Jos onnistut ratkaisemaan SL-haasteen vaikeimmat tehtävät, hallitset säännölliset lausekkeet riittävän hyvin kaikkiin käytännön ohjelmointitilanteisiin.
Viikonpäivän tarkistavassa koodiesimerkissä on varmaan muuttujan $kello tilalla oltava muuttuja $paiva.
Kiitos huomiosta, korjasin virheen.
"Lisäksi mahdollisten säännöllisten lausekkeiden erikoismerkkien eteen täytyy laittaa merkki \."
Vanhassa oppaassa oli joitakin yleisimpiä lueteltuina, mutta mielestäni myös tässä olisi hyvä mainita muutama
edit: niin tai siis kaikki . \ + * ? [ ^ ] $ ( ) { } = ! < > | : -
Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.