Ha még nem használt soha reguláris kifejezéseket, akkor ennek a fejezetnek az elolvasása előtt ajánlott az m operátor illetve az s operátor fejezetek elolvasása. Ugyanakkor azok teljes megértéséhez szükséges ennek a fejezetnek az elolvasása. Így tehát nem tudom, hogy melyiket érdemes elolvasni először. Feltehetőleg az egyiket, aztán a másikat, és azután néhányszor újra.
Bevezető
Reguláris kifejezésre rendkívül egyszerű példát mutatni. A következő példa a @mail
tömb elemeiről próbálja meg eldönteni, hogy jók lesznek-e eMail címnek.
@mail = (
'verhas@mail.digital.hu',
'hab.akukk%mikkamakka@jeno',
);
for( @mail ){
if( /^.*@\w+\..+$/ ){
print "$_ jó email címnek látszik\n";
}else{
print "$_ Ez nem jó cím\n";
}
}
Az első utasítás a @mail tömböt hozza létre. Ennek elemein megy végig a for ciklus, és
minden egyes elemet megvizsgál, és megpróbálja eldönteni, hogy jó lesz-e elektronikus levélcímnek vagy sem.
Mivela for ciklusnak nem adtunk meg változót, ezért a Perl a $_ változót használja ciklus
változónak, és mivel az if utasításban sem adjuk meg, hogy mely változót akarjuk a reguláris
kifejezéshez hasonlítani, ezért ez is ezt a változót használja. (Minő véletlen egybeesés.)
A példabeli reguláris kifejezés szavakkal elmondva a következőt jelenti:
verhas@mail.digital.hu jó email címnek látszik hab.akukk%mikkamakka@jeno Ez nem jó címA reguláris kifejezéseket, mint a fenti példában is látható használhatjuk arra, hogy bizonyos szintaktikus szabályoknak való megfelelést vizsgáljunk füzéreknél. Ehhez a m operátort használtuk a fenti példában. Magát az operátort azért nem kellett kiírni, mert a / jelet használtuk határolónak, a fűzért pedig azért nem, mert az a default változó, a $_ volt. Az if utasításbeli kifejezést írhattuk volna
$_ =~ m/^.*@\w+\..+$/alakban is.
A másik lehetőség reguláris kifejezés használatára, amikor bizonyos részeket ki akarunk cserélni
egy fűzérben. Nézzük példának a következő kis programot:
$text = 'Java szigetén nem használnak JavaScript programokat.';
$text =~ s/Java(?!Script)/Borneo/;
print $text;
aminek a kimenete
Borneo szigetén nem használnak JavaScript programokat.
Az s operátor egy reguláris kifejezést és egy
interpolált füzért használ. Végignézi a változót, amely a példában
$text és az első olyan részfüzért, amely megfelel a reguláris kifejezésnek kicseréli a
második paraméterként megadott füzérre. Így lesz Java szigetéből Borneó.
Érdemes megfigyelni, hogy ezeknél a műveleteknél nem = jelet használunk, hanem =~
operátort. Ezt pedig nem szabad összetéveszteni a ~= operátorral! (A nyelv szépségei!)
Ha ugyanis valaki az előbbi példában a
$text = s/Java(?!Script)/Borneó/;
írná, akkor a helyettesítési operátor (s///) nem a $text változót, hanem a
default $_ változót keresné, amelyben nem talána egyetlen Java alfüzért sem, és
visszatérési értékként üres füzért ad vissza. Más szavakkal: a program nem írna ki semmit,
vagy precízebben semmit írna ki.
Részletesen és precízen az m operátorról, és a s operátorról. Ezeket egy kicsit nehéz megérteni a reguláris kifejezések ismerete nélkül, de vígasztaljon, hogy a reguláris kifejezéseket pedig nehéz megérteni az m és az s operátorok ismerete nélkül.
Amikor a Perl egy füzért megpróbál hozzáilleszteni egy reguláris kifejezéshez mind a füzérben, mind pedig a reguláris kifejezésben balról jobbra halad, egészen addig, amíg el nem ér a reguláris kifejezés vagy a füzér végére. Ha ekkor az egész reguláris kifejezést hozzá tudta illeszteni a füzérhez, akkor a füzér megfelel a reguláris kifejezésnek, ha nem, akkor pedig nem. (Például az m operátor igaz, vagy hamis értéket ad vissza.)
A meta karakter fogalma
Az illesztés során a reguláris kifejezésben minden közönséges karakter, amelyik nem metakarakter (definíció
mindjárt) önnmagának felel meg. Ha tehát a reguláris kifejezés következő karaktere egy 'a' betű,
akkor a füzér következő illesztendő karaktere is 'a' betű kell, hogy legyen.
A metakarakterek speciális karakterek, amelyek a reguláris kifejezésben speciális jelentőséggel bírnak. Ilyen például a . pont, amely a füzérben bármilyen nem újsor karakternek megfelel. Egy másik speciális karakter a $ jel, amelyik a füzér végének, vagy a füzér végén lévő újsor karakternek felel meg. Hasonlóan a ^ karakter a füzér elejének felel meg. Ezt a viselkedést mind az m, mind pedig az s operátoroknál változtatni lehet az m és s opciókkal. Az m opció esetén a füzért többsorosnak tekinti a rendszer, ami praktikusan annyit jelent, hogy minden sor végét meg lehet találni a $ jellel, ami a reguláris kifejezésben a sor vagy a füzér végét jelenti, és hasonlóan minden sor elejét a ^ jellel. Ennek az ellentéte a s opció, amely esetben a füzért egysorosnak tekinti a Perl, és a $ csak a füzér végét fogja megtalálni, vagy a füzér végén álló soremelést a ^ jel pedig csak a füzér elejét úgy, mintha sem az s sem pedig a m opciót nem adtuk volna meg. Az s opció igazi értelme, hogy ennek megadásakor a reguláris kifejezésekben a . pont karakter megfelel a soremelés karaktereknek is, míg enélkül az opció nélkül ezeknek a karaktereknek nem felel meg ez a metakarakter.
Az egyik legfontosabb metakarakter a \ amelyet ha egy metakarakter elé írunk a füzérben a karakter pontosan önnmagának fog megfelelni, így \. egy pontot jelent a füzérben vagy \\ egy fordított törtvonal karaktert. Ha nem metakarakter elé írjuk a fordított törtvonalat, akkor előfordulhat hogy más jelentést kap a karakter, például \t a tabulátor, vagy \w az betű karaktereknek felel meg, de erről még később.
Zárójelek
A zárójelek is speciális jelentéssel bírnak. Ezekkel a reguláris kifejezés egyes részeit lehet bezárójelezni,
és ezekre a részekre lehet a későbbiekben hivatkozni a $1, $2, ... változókkal a reguláris
kifejezésen kívül, vagy a \1, \2, ... referenciákkal a reguláris kifejezésen belül. Példaképpen
$text = 'Java szigetén nem használnak szigonyt.';
$text =~ /(Ja(va)).*(szi).*\3(g(e|o))/;
print "$1 $2 $3 $4 $5\n";
kimenete
Java va szi go o
Látható, hogy a számozási sorrend a nyitó zárójelek szerint megy, és nem a záró zárójelek szerint. Ennek
csak abban az esetben van jelentősége, ha, mint ebben a példában, egymásba vannak ágyazva a zárójelek.
Lehetőség van arra is, hogy úgy zárójelezzünk be egy részt a reguláris kifejezésből, hogy az ne hozzon
létre referenciát. Ehhez a (?: nyitó karakter sort és a szokásos ) zárójelet kell
használni, például:
$text = 'Java szigetén nem használnak szigonyt.';
$text =~ /(Ja(?:va|vi)).*(szi).*\2(g(e|o))/;
print "$1 $2 $3 $4\n";
kimenete
Java szi go o
Itt rögtön látunk egy új metakaraktert, a | jelet. Ez a jel választást jelent a bal és a jobb
oldalán álló rész reguláris kifejezések közül bármelyik megfelelhet a füzér éppen soron levő
darabjának.
Karakter osztály
Még egy érdekes zárójel van a reguláris kifejezésekben, a [ és ]. Ezek kartakter osztályokat
fognak közre. Ha egy reguláris kifejezésben azt írjuk, hogy [acfer], akkor az az a, c,
f, e és r betűk bármelyikének megfelel. Ugyanakkor rövidíthetünk is karakterosztályok
megadásánál. Például [a-f] az összes a és f közötti karakternek felel meg, vagy
[a-fA-F] ugyanez a karakter osztály, de a kisbetűk és a nagybetűk is. Ha a legelső karakter a
karakterosztály megadásánál a [ karakter után egy ^ karakter, akkor a karakter osztály
invertálódik, azaz [^a-f] bármilyen karakternek megfelel, kivéve az a és f közötti
karaktereket. Karakterosztály megadásánál a | karakter is közönséges karakternek számít.
Ismétlés
Egy reguláris kifejezés egy részére, vagy az egész reguláris kifejezésre előírhatjuk, hogy hányszor kell
egymás után alkalmazni. Ezt a reguláris kifejezések ismétlést előíró modosítóival tehetjük meg, amelyeket
mindig a reguláris (rész)kifejezés után kell írni. Ezek:
* | nulla vagy többszöri ismétlés |
+ | egy, vagy többszöri ismétlés |
? | nulla, vagy egyszer |
{n} | pontosan n-szer |
{n,} | legalább n-szer |
{n,m} | legalább n-szer, de legfeljebb m-szer |
Alaphelyzetben ezek az ismétlési módosítók falánkak, és annyira előreszaladnak, amennyire csak lehet.
Ezt lehet visszafogni azzal, ha az ismétlési módosító mögé egy ? jelet írunk. Ekkor csak
annyit ismételnek ezek a módosítók, amennyit feltétlenül kell. Példaképp egy reguláris kifejezés
illesztés * és *? módosítóval
'Java szigetén nem használnak szigonyt.' =~ /Ja(.*)g(?:e|o)/;
print "$1\n";
'Java szigetén nem használnak szigonyt.' =~ /Ja(.*?)g(?:e|o)/;
print "$1\n";
és a kimenete
va szigetén nem használnak szi
va szi
Speciális karakterek
Mivel a reguláris kifejezések mint interpolált füzérek kerülnek kiértékelésre, ezért a következők is
használhatók:
\t | tabulátor (HT, TAB) |
\n | újsor, soremelés (LF, NL) |
\r | kocsivissza karakter (CR) |
\f | lapdobás (FF) |
\a | csengő (bell) (BEL) |
\e | escape (ESC) |
\033 | oktális kód megadása (mint a PDP-11 nél) |
\x1B | hexa karakter |
\c[ | kontrol karakter |
\l | a következőkarakter kisbetűsre konvertálva |
\u | a következő karakter nagybetűsre konvertálva |
\L | kisbetűsek a következő \E-ig |
\U | nagybetűsek a következő \E-ig |
\E | kisbetű, nagybetű módosítás vége |
\Q | reguláris kifejezések meta karakterei elé fordított törtvonalat ra a következő \E-ig |
\w | egy szóban használható karakter (alfanumerikus karakterek és aláhúzás) |
\W | minden ami nem \w |
\s | szóköz fajta karakter, szóköz, tabulátor, újsor stb. |
\S | minden ami nem \s |
\d | számjeg |
\D | nem számjegy |
A következők is használhatók még reguláris kifejezésekben:
\b | szó határának felel meg |
\B | nem szó határnak felel meg |
\A | csak a füzér elején |
\Z | csak a füzér végén |
\G | ott folytatja ahol az előző m//g abbahagyta |
A \A és \Z ugyanaz, mint a ^ és a $ azzal a különbséggel, hogy ezek még a m opció használata esetén sem felelnek meg a füzér belsejében levő soremelés karakternek.
Egyéb kiterjesztések
A reguláris kifejezések a Perl nyelvben a szokásos egyéb UNIX eszközökhöz képest további
kiterjesztéseket is tartalmaznak. Így reguláris kifejezésen belül lehet használni
$t = 'jamaica rum rum kingston rum'; $t =~ s/([aeoui])(?=\w)/uc($1)/ge; print $t;kimenete
jAmAIca rUm rUm kIngstOn rUmami azt jelenti, hogy a mintaillesztés során a Perl megnézte, hogy betű áll-e a magánhangzó után, de azt a betűt, már nem tekintette az illesztett, és így helyettesítendő füzérdarab részének. Ennek megfelelően ez a kis programdarab minden olyan magánhangzót nagybetűre cserélt, amely nem szó végén állt.
$t = 'jamaica rum rum kingston rum'; $t =~ s/([aeoui])(?!\w)/uc($1)/ge; print $t;kimenete
jamaicA rum rum kingston rumami minden olyan magánhangzót cserél nagyra, amelyet nem betű követ. Nagyon fontos megjegyezni, hogy ezekkel a konstrukciókkal csak előre lehet nézni, visszafelé nem. Ha ugyanis azt írjuk, hogy (?!Borneo)Script, akkor az nem azt jelenti, hogy minden olyan Script amely előtt nem Borneo áll. Ez a reguláris kifejezés minden Script-et meg fog találni, a BorneoScript-eket is, illetve annak Script részét, hiszen amikor a (?!Borneo) részt értékeli ki a Perl azt fogja találni, hogy a vizsgált helyen nem Borneo áll, hanem Script. Ez így neki rendben van, megy tovább és ismételten látni fogja, hogy az adott helyen Script áll, ezt most már illeszti is a reguláris kifejezés további részéhez, hiába állt a Script előtt Borneo.
$t = 'jAmAIca rUm rUm kIngstOn rUm'; $t =~ s/(?i)ica/juli/g; $t =~ s/RuM(?i)/bor/; print $t;hatására miért csak az utolsó rUm-ból lesz bor:
jAmAjuli rUm rUm kIngstOn bor
$text = 'Java szigetén nem használnak JavaScript programokat.'; $text =~ s/Java(?=Script)/Borneo/; print $text;példában elindul a mintaillesztés a Perl eljut egészen az első Java végéig, és illeszti a reguláris kifejezést. Itt azonban elakad, mert a (?=Script) megkövetelné, hogy a Java szó után a Script szó következzen. Ezért visszalép és próbálja a füzér további részéhez illeszteni a reguláris kifejezést, amíg el nem jut a JavaScript szóhoz, amelyhez sikerül az illesztés, és ennek megfelelően megtörténik a csere, így az eredmény
Java szigetén nem használnak BorneoScript programokat.Egy másik példát megnézve
$t = 'Mi van a jing és a jang között? Talán a Jangce?'; $t =~ /jing(.*)jang/i; print $1;az eredmény
és a jang között? Talán aEnnek az az oka, hogy amikor a jing szót megtalálja az illesztés a .* részkifejezésseé addig rohan előre, ameddig csak tud. Először a füzér végéig. Itt azonban elakad, és elkezd visszafelé lépkedni. Egészen a Jangce szóig, aholis sikeres a reguláris kifejezés maradék részének az illesztése is.
Ismét egy példát nézve a
$_ = "Van 1 számom: 53147";
if ( /(.*)(\d*)/ ) {
print "Ami van: <$1>, a szám pedig <$2>.\n";
}else{
print "nincsen számom...\n";
}
az eredménye
Ami van: <Van 1 számom: 53147>, a szám pedig <>.
aminek az oka, hogy a \d* hozzáilleszthető a füzér végén egy üres füzérhez. Helyesen:
$_ = "Van 1 számom: 53147";
if ( /(.*\D)(\d+)/ ) {
print "Ami van: <$1>, a szám pedig <$2>.\n";
}else{
print "nincsen számom...\n";
}
az eredménye
Ami van: <Van 1 számom: >, a szám pedig <53147>.