Tämä on kädestä pitäen -johdatus Perl-ohjelmointikieleen. Pääpaino on kielen yksinkertaisimmilla rakenteilla, ymmärrettävällä ohjelmointityylillä (lyhyyteen pyrkimättä) ja kielen soveltamisella pienehköjen, usein kertakäyttöisten työkalujen tekemiseen.
Tässä oppaassa on yritetty olla edellyttämättä aiempaa kokemusta ohjelmoinnista. Tässä ei edes verrata Perliä muihin kieliin, vaan mahdolliset yhtäläisyydet (ja erot) jätetään muita kieliä tuntevien lukijoiden itsensä oivallettaviksi. Jos tahti on sinulle liian hidas ja osaat englantia, harkitse siirtymistä lukemaan tiiviimpää tutoriaalia Perl Lessons.
Lukijan oletetaan kuitenkin tietävän jotain tietokoneista, ainakin osaavan käyttää jotakin tekstieditoria sekä tietävän, miten komentorivikomentoja annetaan siinä järjestelmässä, jota hän käyttää. Jos olet esimerkiksi Windows-käyttäjä, sinun siis pitäisi tietää, miten kirjoitetaan tekstitiedosto esim. NotePad- eli Muistio-ohjelmalla ja miten avataan komentoikkuna ("DOS-ikkuna"). Jos taas olet Unix-käyttäjä, niin luultavasti et ole voinut olla oppimatta tässä tarvittavia vastaavia asioita.
Tässä ensimmäinen Perl-esimerkkiohjelmamme:
while(<>) { print; }
Tämä ohjelma tekee aika yksinkertaisen asian: se lukee tekstirivejä ja tulostaa ne. Emme vielä paneudu ohjelman sisäiseen rakenteeseen, vaan otamme sen tässä vain yhtenä Perlille tyypillisenä ilmaisuna, idiomina. Vihjaamme kuitenkin osien merkitykseen kertomalla, miten eri osat voidaan lukea:
while | niin kauan kuin... |
(<>) | ...rivin lukeminen onnistuu (rivit eivät ole loppuneet)... |
{ | ... suorita seuraava:... |
print; | ... tulosta (juuri lukemasi rivi)... |
} | (loppu) |
Kummallisin tässä kai on rakenne (<>)
,
mutta se on toisaalta rakenne, joka tekee tällaisten ohjelmien
kirjoittamisen Perlillä huomattavan näppäräksi verrattuna
useimpiin muihin kieliin. Ja silläkin on oma selityksensä.
Ohjelma on erikoistapaus seuraavanlaisesta yleisestä muodosta,
while
-toistorakenteesta:
while(ehto) { toiminto }
Välimerkit ovat tässä olennaisia. Tavalliset
sulkeet ()
tarvitaan aina ehdon ympärille. Ja
aaltosulkeet {}
rajaavat sen, mikä kaikki ohjelmassa
on kyseisen toiston piirissä. Ohjelmassahan saattaa myöhemmin
olla muita toimintoja, joita ei pidä toistettaman; voisimme esimerkiksi
lisätä ohjelman loppuun lauseen
print 'Loppu';
jota ei tietenkään pidä toistaa! Ja homma hoituu, kunhan se kirjoitetaan
edellä mainitun loppuaaltosulkeen }
jälkeen.
Huomaa, että ohjelman jakeminen eri riveille ja sisentäminen
ei ole sen toiminnan kannalta merkitsevää vaan tehdään, jotta
ihmisen on mukavampi lukea ohjelmaa
ja hahmottaa sen rakenne.
Ohjelman toiminta ei kuulosta kovinkaan hyödylliseltä - tai pikemminkin tulee mieleen, että sellainenhan voidaan tehdä jollakin valmiilla komennolla tai muulla toimenpiteellä ilman mitään ohjelmointia. Totta. Mutta kun tällainen ohjelman runko on valmiina, niin siihen voi sitten lisätä kaikenlaista rivien muokkausta ennen tulostusta. Esimerkiksi näin:
while(<>) { s/!+/!/g; print; }
Tässä on ohjelmaan lisätty ihmeellisen loitsun näköinen
s/!+/!/g;
jonka vaikutus on se, että se korvaa jokaisen tekstissä olevan
peräkkäisten huutomerkkien jonon yhdellä huutomerkillä. Siis
"Hei!!! Auttakaa mua!!!!" muuntuu siistimpään
muotoon "Hei! Auttakaa mua!".
Loitsu on korvauskäsky eli
s
-käsky, ja se on asiaa
tuntemattomalle hämärän näköinen, koska se on niin lyhyt, ja se
on lyhyt, jotta se olisi nopea kirjoittaa. Tällaiset käskyt
nimittäin ovat Perlille hyvin ominaisia ja paljon käytettyjä.
Katsotaanpa, mitä esimerkkikäsky oikein sisältää:
s
kertoo, millaisesta
käskystä on kyse; se johtuu englannin sanasta
substitute 'korvata'
/
toimii rajoittimena: ensimmäisen
ja toisen vinoviivan välissä on korvattava teksti
(tässä !+
), ja toisen ja kolmannen vinoviivan
välissä on korvaava teksti (tässä !
)
g
on ns. valitsin, joka
vaikuttaa sen, että korvaus tapahtuu kaikkialla rivillä, ja
johtuu englannin sanasta global 'yleinen, kaikenkattava';
jos g
puuttuisi, niin korvaus kohdistuisi vain
ensimmäiseen korvattavan merkkijonon esiintymään rivillä
;
on yleinen lauseiden
lopetin Perlissä ja verrattavissa pisteen käyttöön suomen kielessä
virkkeiden lopussa.
Käsky on siis seuraavaa yleistä muotoa:
s/korvattava/korvaava/valitsimia;
Mutta mitä kummallinen !+
tarkoittaa? Eikös ollut
kyse huutomerkkijonojen korvaamisesta? Aivan. Tällaisissa yhteyksissä,
siis korvauskäskyssä korvattavan merkkijonon paikalla, joillakin
merkeillä on erikoismerkitys. Erityisesti +
ei tarkoitakaan itseään, vaan se tarkoittaa edellä olevan merkin
mahdollista toistumista. Siis !+
tarkoittaa
mitä tahansa merkkijonoista !
, !!
,
!!!
, !!!!
jne.
Tarkkaavainen lukija tulee ehkä ajatelleeksi, että edellä
käytetty käsky
aiheuttaa turhaakin työtä, nimittäin myös yhden huutomerkin korvaamisen
yhdellä huutomerkillä. Totta. Se ei kuitenkaan ole vakavaa, ainakaan
tässä. Se voitaisiin välttää monella tavalla, yksinkertaisimmin kai
kirjoittamalla s/!!+/!/g;
.
Merkit, joilla on erikoismerkitys korvauskäskyissä, ovat seuraavat:
Niiden käyttöön tutustutaan tarkemmin jäljempänä.
Mutta joku kuitenkin haluaa tietää, mitä tehdä sitten, jos
haluaakin vaikkapa korvata plusmerkit jollakin, esimerkiksi
merkkijonolla "ynnä". Ja vastaus on, että merkin erikoismerkityksen
voi poistaa kirjoittamalla sen eteen kenoviivan \, joka siis
toimii tavallaan lainausmerkkinä:
s/\+/ynnä/g;
Vaikeaa? No, korvauskäsky voisi toki olla paljon yksinkertaisempikin,
sellainen, jossa ei ole mitään erikoismerkkejä:
s/Jukka/Yucca/g;
Tämä korvaisi merkkijonon "Jukka" jokaisen esiintymän merkkijonolla
"Yucca". Huomaa, että esimerkiksi merkkijonot "jukka" ja "Jukan"
jäisivät muuttumatta ja että "Jukkaa" muuttuisi muotoon "Yuccaa", mutta
vain siksi, että sen osana on "Jukka". Korvauskäsky käsittelee
merkkien jonoja kiinnittämättä mitään huomiota merkkien
mahdolliseen merkitykseen tai edes siihen, muodostavatko ne jotain
sanan näköistä.
Nyt on aika kokeilla rauhassa. Kirjoitapa aluksi pieni tekstitiedosto,
jossa on esimerkkiaineistoa edellä kuvatuilla pikku ohjelmilla
käsiteltäväksi. Vaikkapa seuraavansisältöinen teksti, vaikkapa
tiedostoon nimeltä ekakoe.txt
:
Hei, minä olen esimerkkitiedosto!!! Tää on kivaa!! Jukka on hauska nimi. Moni Jukkakin on hauska!! Siinä kaikki!!
Kirjoita sitten toiseen tiedostoon, nimeltään vaikkapa
ekakoe.pl
, edellä esitetty huutomerkkienkorvausohjelma:
while(<>) { s/!+/!/g; print; }
Sitten kokeilemaan. Ai niin, siihen tarvitaan Perl-kielen toteutus,
Perl-tulkki (interpreter) eli ohjelma, joka lukee
Perl-ohjelmia ja suorittaa ne. Tavallisesti Perl-tulkkia käytetään
komentorivitilassa eli järjestelmän komentotasolla
annetaan esim. komento
perl ekakoe.pl <ekakoe.txt
jolloin kuvaruudulle pitäisi tulostua esimerkkiteksti sellaisena,
kuin se on Perl-ohjelmamme tekemän muokkauksen jäljiltä.
Jos käytät Windowsia,
komentorivitila tarkoittaa ns. DOS-tilaa
(Command Prompt, "DOS-tila"), johon päästään esim.
Start- eli Käynnistä-valikon kautta (kohdasta Programs, Ohjelmat).
Sitten vain siirryt (lähinnä CD
-käskyillä) siihen
hakemistoon, jossa Perl-ohjelmasi on, ja annat edellä kuvatun
tapaisen komennon.
Mutta on hyvin mahdollista, että
joudut hankkimaan ja asentamaan Perl-tulkin, ennen kuin voit
harjoitella Perl-ohjelmilla.
Näin nimittäin on, jos et ole itse asentanut
Perl-tulkkia koneeseesi eikä kukaan muukaan ole sitä tehnyt;
itse Windowsin mukana ei tule Perl-tulkkia.
Toisaalta kyse ei ole niin isosta asiasta kuin voisi luulla, ja se
tarvitsee tehdä vain kerran.
Tietoja siitä, mistä saa Perl-tulkkeja
eri järjestelmiin, löytyy osoitteen
http://www.perl.com/pub/language/info/software.html
kautta. Erinomainen vaihtoehto Windowsille (ns. 32-bittisille
Windowseille: Windows 95, Windows 98, Windows NT, Windows 2000) on
ActivePerl, osoite:
http://www.activestate.com/Products/ActivePerl/
Valitettavasti kyseessä on isohko ohjelma, joten sen imuroimiseen
Internetistä menee aikaa varsinkin modemiyhteydellä; kannattaa ehkä
kysellä lähiympäristöstä, olisiko jollakulla tutulla
ActivePerl-jakelupakettia
CD-romilla. (ActivePerliä saa kopioida ja jakaa
varsin vapaasti, ks. lisenssiehtoja.)
Itse asennus on aika helppo; jos olet joskus asentanut
Windowsiin ohjelmia, homman pitäisi olla
helppo nakki. Jos et, on aika opetella.
Jos taas käytät jonkinlaista
Unix-konetta,
siinä luultavasti on Perl-tulkki valmiina, yleensä nimellä perl
.
Voit kokeilla antaa (Unixin komentotasolla, "shellissä") komennon
perl ekakoe.pl <ekakoe.txt
(Komentotasolle pääset Unixissa poistumalla siitä ohjelmasta, jossa
olet, esim. editorista, jollakin ohjelmakohtaisella tavalla.)
Jos tämä ei toimi, ota yhteys koneen
ylläpitoon ja tiedustele, miten saat Perl-tulkin käyttöösi.
Se saattaa olla asennettuna niin, että joudut tekemään jonkin erityisen
toimenpiteen voidaksesi käyttää sitä.
No, miten kävi? Toivottavasti jotenkin näin (esimerkki on tässä ajettu eräässä Unix-koneessa):
gamma perl 71 % perl ekakoe.pl <ekakoe.txt Hei, minä olen esimerkkitiedosto! Tää on kivaa! Jukka on hauska nimi. Moni Jukkakin on hauska! Siinä kaikki! gamma perl 72 %
Kun haluat kokeilla Perl-ohjelmia, jotka tulostavat paljon tekstiä,
kannattaa ehkä ohjata tulostus tiedostoon. Arvaat
ehkä, kuinka.
Kuten rakenne <
tiedostonnimi edellä
mainitulla komentorivillä ohjaa Perl-ohjelman lukemaan lähtöaineiston
tiedostosta tiedostonnimi, voimme vastaavalla rakenteella
mutta >
-merkkiä käyttäen ohjata tulostuksen
haluttuun tiedostoon. Esimerkki:
perl ekakoe.pl <ekakoe.txt >ekakoe.tul
Tämä on Perl-tulkille (nimeltään perl
) annettu
käsky lukea tiedostossa ekakoe.pl
oleva Perl-ohjelma
ja suorittaa se, tarkemmin sanoen siten, että lähtöaineisto tulee
lukea tiedostosta ekakoe.txt
ja tulokset kirjoittaa
tiedostoon ekakoe.tul
.
Perl-ohjelmassa ei siis tarvitse lyödä lukkoon, minkänimisestä tiedostosta se lukee lähtöaineiston ja minkänimiseen tiedostoon se kirjoittaa tuloksensa. Myöhemmin opimme, että niin toki voidaan tehdä - ja usein kannattaakin tehdä. Mutta toistaiseksi oletamme, että tiedostot ilmoitetaan itse Perl-ohjelman ulkopuolella edellä kuvatulla tavalla. Itse asiassa voimme kyllä jättää ne ilmoittamattakin eli käynnistää ohjelman yksinkertaisesti tyyliin perl ekakoe.pl, jolloin ohjelmalle pitää antaa lähtöaineisto suoraan näppäimistöltä näpyttelemällä ja se kirjoittaa tulokset suoraan kuvaruutuun.
Nyt voisit jo
harjoitella yksinkertaisia tekstiaineiston muunnoksia
itse tekemilläsi Perl-ohjelmilla.
Huomaa, että yhden s
-käskyn jälkeen, ennen
print
-käskyä, voi olla lisää s
-käskyjä,
jotka muokkaavat käsittelyssä olevaa riviä lisää.
Ehkäpä haluaisit kokeilla voimiasi seuraavaan tehtävään. Sellaista
oikeasti tarvitaan
eräissä yhteyksissä
Web-sivujen teossa; emme
kuitenkaan nyt puutu siihen,
miksi homma tehdään, koska
se ei ole olennaista tälle harjoittelulle, vaan annamme vain
teknisen kuvauksen siitä, mitä pitää tehdä:
&
jokainen esiintymä
alkuperäisessä tekstissä
merkkijonolla &
<
merkkijonolla <
>
merkkijonolla >
<pre>
ja
loppuun </pre>
Vakiomerkkijono
voidaan tulostaa käskyllä, joka on muotoa
print 'merkkijono';
(Huomaa, että merkkijonon ympärillä on heittomerkit,
ei aksenttimerkkejä. Tavallisessa suomalaisessa näppäimistössä
heittomerkin saa näppäimestä, joka on ä-näppäimen oikealla puolella.)
Ja koska kyseiset tulostukset pitää tehdä vain kerran eikä jokaista
syöttötietoriviä kohti, niiden paikka on koko while
-silmukan
ulkopuolella.
Kokeilepa ratkaista tehtävä itse, ja testaa ohjelmaasi vaikkapa lähtöaineistolla
Hauska <em>Testi</em>. Oikein "hauska". Loppu & slut.ja vasta sitten vertaa ratkaisuasi malliratkaisuun ja sen tulosteeseen
Seuraavaksi siirrymme oikeastaan yksinkertaisempiin
asioihin. Edellä käsitelty s
-käsky
kuten s/Jukka/Yucca/
tekee kaksi asiaa:
Jukka
Tunnistuskäsky eli m
-käsky tekee
näistä vain ensimmäisen. Koska korvaavaa merkkijono ei ole, rakenne
on yksinkertaisesti
m/
merkkijono/valitsimia
mistä valitsimet voivat puuttuakin. Esimerkiksi
m/Jukka/
yksinkertaisesti tunnistaa, esiintyykö käsittelyssä olevassa merkkijonossa
jossakin kohtaa merkkijono Jukka
(juuri tässä muodossa).
Käskyn nimi johtuu
englannin sanasta match.
Tunnistamisesta ei tietenkään ole hyötyä, ellei tietoa sen
onnistumisesta käytetä mihinkään. Niinpä tunnistuskäskyä käytetäänkin
useimmiten ehtolausekkeena, jonka arvo eli se,
onnistuuko tunnistus, vaikuttaa siihen, mitä ohjelma seuraavaksi tekee.
Tarkkaavainen lukija on ehkä huomannut, että edellä ei ollut
puolipistettä käskyn lopussa. Itse asiassa syynä on juuri se, että
m
-käskyä käytetäänkin tavallisesti lausekkeena, jolloin
sen perään ei kuulu puolipistettä.
Erittäin tavallinen rakenne, jossa käytetään ehtolauseketta,
on if
-lause, joka
yksinkertaisimmillaan on muotoa
if(
ehtolauseke) {
lausejono}
ja se aiheuttaa ehtolausekkeen arvon laskemisen, jonka jälkeen
Perl-tulkki suorittaa lausejonon, jos tulos oli "tosi",
muussa tapauksessa ei seuraa mitään (vaan ohjelman suoritus jatkuu
seuraavasta lauseesta).
Seuraava esimerkkiohjelma on muokattu
alussa esittämästämme yksinkertaisesta
kopiointiohjelmasta tekemällä käskyn print;
suoritus ehdolliseksi:
while(<>) { if(m/Jukka/) { print; } }
Muistutamme, että sisennykset eivät vaikuta ohjelman merkitykseen vaan ratkaisevaa on aaltosulkeiden käyttö. Tämäntapainen sisentely kuitenkin auttaa ohjelmaa lukevaa ihmistä hahmottamaan sen rakenteen - jos nimittäin sisennykset on tehty rakennetta vastaavalla tavalla!
Tämä ohjelma lukee lähtöaineistonsa rivi kerrallaan ja tulostaa
ne rivit, joilla esiintyy jossakin kohdassa merkkijono Jukka
.
Tämäntapainen ohjelma voi siis toimia "suodattimena", joka poimii
tiedostosta vain jonkin ehdon täyttävät rivit. Ehto voi olla hyvinkin
monimutkainen, tai hyvin yksinkertainen kuten tässä.
Jos haluaisimme, että ohjelma tekemässään tunnistuksessa
jättää isojen ja pienten kirjainten eron huomiotta
(engl. case-insensitive match) eli esimerkkitapauksessamme
tunnistaa myös rivit, joilla esiintyy vaikkapa JUKKA
tai jukka
tai juKkA
, niin kirjoittaisimme
vain m
-käskyn loppuun kirjaimen i
,
while(<>) { if(m/Jukka/i) { print; } }
Vastaavasti voisimme kirjoittaa s
-käskyn
s/Jukka/Yucca/i
, mutta silloin i
:n vaikutus
kohdistuisi vain tunnistukseen, ei korvaamiseen. Esim. JUKKA
ei korvautuisi merkkijonolla YUCCA
vaan kiinteästi
merkkijonolla Yucca
.
(Jatkuu ehkä joskus. Seuraavaksi: säännölliset lausekkeet.)
Viimeisimmän päivityksen ajankohta: 2000-08-08
Jukka Korpela