A regexről – másképp
Aki valaha is használt már számítógépet az szinte minden bizonnyal találkozott már reguláris kifejezésekkel, sőt használta is azokat. Tették mindezt annak ellenére, hogy tudták volna mivel is van dolguk. Ebben a cikksorozatban elmélyedünk ennek a páratlanul hatékony és egyben páratlanul sok fejtörést okozó eszköznek a lehetőségeiben, miközben tisztázunk néhány homályos pontot, és eloszlatunk néhány gyakori félreértést. Az első részben mellőzzük a szokásos "gyorstalpalós" ismertőket metakarakterekről, módosítókról és egyebekről. Ennél mélyebb összefüggéseiben tárjuk fel a regexek jelentőségét és helyét a programozásban.Ezek a műveletek olyan alapszintűek, hogy biztos vagyok az előző állításom igazában. Homokba dughatjuk a fejünket, de a helyzet ettől nem fog változni. Akkor mi is az a regex mátrix? Ennek a bevezető cikknek nem célja a történeti áttekintés, a regex fejlődésének tárgyalása, ezt majd a sok tudástól meggyötörten menetközben szedjük magunkra pihenőidőben. A név - reguláris kifejezés - azonban némi magyarázatot igényel(ne).
Az angol szakirodalom "regular expression" néven említi ezt a konstrukciót, ebből keletkezett fél-tükörfordítással a magyar megfelelője. Van elfogadható magyarázat arra, hogy a "reguláris" miért maradt ebben az alakban, de jobb, ha ezt most még nem feszegetjük. (Ellenkező esetben nagy eséllyel itt abbahagyná majdnem mindenki a cikk olvasását, pedig nem azért írtam.) Annyit azonban érdemes tenni, hogy valami értelmes magyar megfelelőt találunk helyette. Ehhez nem árt tudnunk, hogy mire is jó a regex? A regex tulajdonképpen egy szűrő, amivel néhány vagy akár tömérdek adat közül kiválogathatjuk a számunkra érdekeseket. (A rövid definicók általában félelmetes bensőt takarnak, és ez most sem lesz másképp.)
Nos, a helyzet korántsem ilyen egyszerű vagy egyértelmű, ezért is időzünk el felette még mindig. A következő példa is biztosan ismerős lesz mindenki számára. Írjunk két rövid programocskát -- két parancsállományt --, amelyek kiírják a kapott paramétereiket. Vizsgáljuk meg a kimenetüket!Windows-on a "program" mindkét esetben a paraméterként kapott regexet írta ki, amiből arra következtethetünk, hogy itt maga a `dir' parancs végzi el a mintaillesztést. A *nix-okon más a helyzet. Az első esetben magát a mintát kapta meg a program, míg a másodszorra már a könyvtárban levő fájlneveket. Ebben a környezetben parancshéj (shell) a program indítása előtt megpróbálja feloldani a mintát, és ha sikerrel jár, akkor lecseréli a paramétert az illeszkedő nevekre. Az igazi csavar azonban az ebben a példában, hogy míg az
Ennél a példánál maradva további különbségeket is fel tudunk sorolni a két rendszer között! Listázzuk ki a (kis)betűvel kezdődő állományneveket:Ha ugyanarra könyvtárra is futtatjuk a két parancsot, akkor is eltér az eredmény. Ez azért van, mert -- felületesen mondva -- a Windows parancsértelmezője nem ismeri a
Először is, létezik maga a reguláris kifejezés/mintaillesztés fogalma. Ez a létező absztrakciók ("lila ködök") egyik legnagyobbika, túl sok minden specifikus nem mondható el róla.
Ennek égisze alatt különféle csoportokról, - jobb híján - a regex "részhalmazairól" beszélhetünk, amelyek még nem egy-egy konkrét megvalósítást jelentenek, inkább azokat összefogó áramlatokat. Ezeket azért különböztetem meg önkényesen, mert azonos célra készült megvalósítások általában több rokonságot és hasonló lefedettséget mutatnak a csoport többi tagjával, mint más csoportok tagjaival. Magyarán szólva, a felhasználási területek mentén is könnyen csoportosíthatók a regex nyelvjárások.
A különböző részhalmazok igencsak sokféleképpen viszonyulnak egymáshoz. A regularitáshoz szükséges minimum funkciókat mindegyiknek teljesítenie kell. Vannak ezen kívül már széles körben elterjedt funkciók, amelyeket szinte mindegyik változat támogat (pl. visszautalás), kivéve a legelemibb alkalmazásokat, mint amilyen a fájlnév-illesztés. Léteznek olyan funkciók is, amelyeket azonban csak néhány részhalmazban találunk meg, mint pl. a hátratekintő vizsgálat, az explicit mohó (másnéven harácsoló) kvantorok vagy a Unicode karakterosztályok támogatása. Meglepő módon azonban néhány ilyen egzotikus lehetőség nem mindig a legnagyobbak kiváltsága: a GNU regex könyvtárat használó
A funkciók megléte/hiánya összefüggésben áll azzal, hogy az adott nyelvjárás milyen metakaraktereket illetve metaszekvenciákat ismer, de nem azonos vele! Vegyük a szöveg elejére illetve végére illeszkedés ismert példáját! Ebben az esetben, a reguláris kifejezésben egy pozíciót "keresünk", nem pedig egyszerűen karaktersorozatra illesztünk. A
A példák a W3C CSS 2.1 ajánlásából valók.
"Hogy kerül a csizma az asztalra?" - kérdezhetnénk. A CSS példákban fókuszáljunk a kiválasztókra, mivel csak azok hordoznak itt önmagukban is értelmes jelentést. A CSS kiválasztókat tualjdonképpen ugyanarra használjuk, mint a regexeket: a bemeneti elemek közül mindkettő kiválasztja a valamilyen szabálynak megfelelőket. Ráadásul ezt a szabály(osságo)t nem algoritmikus formában kell megadni valamilyen programozási nyelven egyik esetben sem. Az ilyen tulajdonságú nyelveket a számítástechnikában deklaratív nyelveknek hívják szemben a procedurális nyelvekkel, mint amilyen a C, PHP, Perl stb. is. Ez utóbbiakban nekünk kell kigondolni, és a vezérlőszerkezetekkel olyan lépéssorozatot felépíteni, amivel kívánt célt elérjük. A deklaratív nyelvek leveszik a vállunkról a terhet azzal, hogy tulajdonképpen számukra csak a célt kell megfogalmazni egy jól formalizált alakban, a cél eléréséhez vezető lépéseket ők határozzák meg.
Ez az analógia azt is megmutatja, hogy a regexmotorok milyen műveleteket végeznek, amikor ezekkel a reguláris kifejezésekkel dolgoznak. A középső oszlopban a folyamatábrára illeszkedő adatfolyamféleségen látjuk, milyen adatok áramolhatnak a hasonló nevű feldolgozási lépésben a folyamatábrán. Innen már csak egy ugrás a harmadik oszlopban látható állapotgráf, amivel az ilyen egyszerű regexet megértő automatákat szokás jellemezni. Ennek az ábrázolásmódnak az értelmezése a következő. Az automata mindig egy körrel jelzett állapotban van. Az állapotok között az éleken keresztül vezett az út, de mindig csak olyan élen léphetünk tovább egy állapotból egy másikba, amelyik élnek címkéje a soron következő karakterrel megyegyezik. (A szagatott vonalak jelzik, hogyan "fordulnak" az adatfolyam csúcsai az állapotgráf éleivé.)
■ A regex mátrix
A reguláris kifejezésekkel mindenki találkozott már, aki valaha is beírt ehhez hasonló parancsot:windows> dir *.php
linux$ ls *.php
"A regex mindenütt ott van. A regex körbevesz minket."
Az angol szakirodalom "regular expression" néven említi ezt a konstrukciót, ebből keletkezett fél-tükörfordítással a magyar megfelelője. Van elfogadható magyarázat arra, hogy a "reguláris" miért maradt ebben az alakban, de jobb, ha ezt most még nem feszegetjük. (Ellenkező esetben nagy eséllyel itt abbahagyná majdnem mindenki a cikk olvasását, pedig nem azért írtam.) Annyit azonban érdemes tenni, hogy valami értelmes magyar megfelelőt találunk helyette. Ehhez nem árt tudnunk, hogy mire is jó a regex? A regex tulajdonképpen egy szűrő, amivel néhány vagy akár tömérdek adat közül kiválogathatjuk a számunkra érdekeseket. (A rövid definicók általában félelmetes bensőt takarnak, és ez most sem lesz másképp.)
A regex káosz
Ahhoz, hogy pontosan megértsük a regex működését, azonosítsuk a rendszer bemeneteit, kimeneteit és a feladatvégzés helyét! Az előző példában egyértelmű, hogy a"*.php"
maga a reguláris kifejezés - regex, de mostantól magunk közt hívhatjuk mintaillesztő kifejezésnek is. (A regexhez hasonló "miki" vagy "milki" rövidítések -- hangzásuk folytán -- nem túl szerencsések.) A bemeneti adatok halmaza is világos: az aktuális könyvtárban levő összes bejegyzés neve. Most már csak az van hátra, hogy azonosítsuk a regexmotort, ami az illesztést elvégzi, vagy legalább az alkalmazást, ami erre neki parancsot ad! Elsőre -- a felfedezés okozta önfeledtségünkben -- rávághatnánk, hogy ezek nem mások, mint a dir
és az ls
.Nos, a helyzet korántsem ilyen egyszerű vagy egyértelmű, ezért is időzünk el felette még mindig. A következő példa is biztosan ismerős lesz mindenki számára. Írjunk két rövid programocskát -- két parancsállományt --, amelyek kiírják a kapott paramétereiket. Vizsgáljuk meg a kimenetüket!
windows> echo echo %* > dir.bat
windows> dir.bat *.php
*.php
windows> dir.bat *.bat
*.bat
linux$ echo 'echo $*' > ls; chmod +x ls
linux$ ./ls *.php
*.php
linux$ ./ls *
ls
ls
közönséges program *nix-okon, addig a dir
a Windows parancsértelmezőjének (shell) a beépített "függvénye", tehát kilyukadunk oda, hogy mindkét esetben a héj végzi el az illesztést, de nem ugyanazon a ponton! Ez a különbség azonban saját programjaink bemenetére van hatással. (Zárójelben jegyzem meg, hogy ez nem minden GNU Linux/Unix héj esetén van így, a példában bash funkcionalitását vettem alapul.) Ennél a példánál maradva további különbségeket is fel tudunk sorolni a két rendszer között! Listázzuk ki a (kis)betűvel kezdődő állományneveket:
windows> dir [a-z]*
A fájl nem található.
linux$ ls [a-z]*
ls
[]
karakterek speciális jelentését, és olyan neveket keres, amelyek '[a-z]'
karaktersorozattal kezdődnek, és valljuk be, ilyen elég kevés akad. A két rendszer által használt regexmotor tehát nem is ugyanazt a "nyelvet beszéli", az egyik olyat is megért, amit a másik nem. Ne feledjük azonban, hogy ha a két regex motor ugyanaz is lenne, akkor is amiatt hogy más ponton aktiválódnak, más - más bemenetet szolgáltatnak tesztprogramjainknak.Fény az éjszakában
Elég sok fogalom keveredett össze az iménti bekezdésekben ideje a dolgokat tisztába tenni!Először is, létezik maga a reguláris kifejezés/mintaillesztés fogalma. Ez a létező absztrakciók ("lila ködök") egyik legnagyobbika, túl sok minden specifikus nem mondható el róla.
Ennek égisze alatt különféle csoportokról, - jobb híján - a regex "részhalmazairól" beszélhetünk, amelyek még nem egy-egy konkrét megvalósítást jelentenek, inkább azokat összefogó áramlatokat. Ezeket azért különböztetem meg önkényesen, mert azonos célra készült megvalósítások általában több rokonságot és hasonló lefedettséget mutatnak a csoport többi tagjával, mint más csoportok tagjaival. Magyarán szólva, a felhasználási területek mentén is könnyen csoportosíthatók a regex nyelvjárások.
A különböző részhalmazok igencsak sokféleképpen viszonyulnak egymáshoz. A regularitáshoz szükséges minimum funkciókat mindegyiknek teljesítenie kell. Vannak ezen kívül már széles körben elterjedt funkciók, amelyeket szinte mindegyik változat támogat (pl. visszautalás), kivéve a legelemibb alkalmazásokat, mint amilyen a fájlnév-illesztés. Léteznek olyan funkciók is, amelyeket azonban csak néhány részhalmazban találunk meg, mint pl. a hátratekintő vizsgálat, az explicit mohó (másnéven harácsoló) kvantorok vagy a Unicode karakterosztályok támogatása. Meglepő módon azonban néhány ilyen egzotikus lehetőség nem mindig a legnagyobbak kiváltsága: a GNU regex könyvtárat használó
egrep
program szóeleji határra (\<
) és szóvegi határra (\>
) illeszkedést nem ismeri még a Perl sem. (Ennek legvalószínűbb oka az, hogy ezek nélkül is jól megvagyunk a sima \b
szóhatárral.) A funkciók megléte/hiánya összefüggésben áll azzal, hogy az adott nyelvjárás milyen metakaraktereket illetve metaszekvenciákat ismer, de nem azonos vele! Vegyük a szöveg elejére illetve végére illeszkedés ismert példáját! Ebben az esetben, a reguláris kifejezésben egy pozíciót "keresünk", nem pedig egyszerűen karaktersorozatra illesztünk. A
grep
által "beszélt" nyelvjárás erre a '^'
és '$'
metakaraktereket használja, míg a PHP PCRE-t értő nyelvjárásban használhatjuk még a \A
és a \Z
jelöléseket is - némi plusz jelentéssel. Mindkét nyelvjárás ismeri tehát a soreleje, sorvég illesztést, de eltérő metakaraktereket használ(hat)nak ennek jelölésére.regex_felho_r.png
CSS analógia
A Weblabor olvasóinak, webfejlesztőknek a fájlnévmintákon túl máshonnan is ismerős lehet a "regex filozófia":h1 em { color: blue }
div p *[href] { }
h1.opener + h2 { margin-top: -5mm }
"Hogy kerül a csizma az asztalra?" - kérdezhetnénk. A CSS példákban fókuszáljunk a kiválasztókra, mivel csak azok hordoznak itt önmagukban is értelmes jelentést. A CSS kiválasztókat tualjdonképpen ugyanarra használjuk, mint a regexeket: a bemeneti elemek közül mindkettő kiválasztja a valamilyen szabálynak megfelelőket. Ráadásul ezt a szabály(osságo)t nem algoritmikus formában kell megadni valamilyen programozási nyelven egyik esetben sem. Az ilyen tulajdonságú nyelveket a számítástechnikában deklaratív nyelveknek hívják szemben a procedurális nyelvekkel, mint amilyen a C, PHP, Perl stb. is. Ez utóbbiakban nekünk kell kigondolni, és a vezérlőszerkezetekkel olyan lépéssorozatot felépíteni, amivel kívánt célt elérjük. A deklaratív nyelvek leveszik a vállunkról a terhet azzal, hogy tulajdonképpen számukra csak a célt kell megfogalmazni egy jól formalizált alakban, a cél eléréséhez vezető lépéseket ők határozzák meg.
Miből lesz a cserebogár?
A procedurális programozás illetve az algoritmizálás rugalmasságát a vezérlőszerkezetek adják, amelyek segítségével a bemeneti adatoktól függően tudunk végrehajtani, kihagyni vagy megismételni egész műveletsorozatokat. A CSS kiválasztók és a regexek esetében ezt a rugalmasságot a mintákban használható vezérlőelemek biztosítják, mint a Jolly Joker a kártyapakliban. (Természetes, hogy lennie kell ilyen képességű elemnek, hiszen bármire illeszkedni képes kifejezéseket szeretnénk használni mind CSS-ben, mind a regexekben.) Talán nem meglepő, hogy az ábrázolásuk is nagyon hasonló. Az alábbi ábrán azt mutatom meg, hogy a folyamatábra két alapvető szerkezete - az iteráció és a feltételes elágazás -, miként "alakul át" a reguláris kifejezések alapját jelentő konstrukciókká: ismétléssé és alternatív mintává.A feltételes elágazás és az alternatív minták (a|b), illetve a ciklus és az ismétlés (*) hasonlósága.
Ez az analógia azt is megmutatja, hogy a regexmotorok milyen műveleteket végeznek, amikor ezekkel a reguláris kifejezésekkel dolgoznak. A középső oszlopban a folyamatábrára illeszkedő adatfolyamféleségen látjuk, milyen adatok áramolhatnak a hasonló nevű feldolgozási lépésben a folyamatábrán. Innen már csak egy ugrás a harmadik oszlopban látható állapotgráf, amivel az ilyen egyszerű regexet megértő automatákat szokás jellemezni. Ennek az ábrázolásmódnak az értelmezése a következő. Az automata mindig egy körrel jelzett állapotban van. Az állapotok között az éleken keresztül vezett az út, de mindig csak olyan élen léphetünk tovább egy állapotból egy másikba, amelyik élnek címkéje a soron következő karakterrel megyegyezik. (A szagatott vonalak jelzik, hogyan "fordulnak" az adatfolyam csúcsai az állapotgráf éleivé.)
Végre egy olyan cikk, amely
Apropó! Ha van valami téma amire kíváncsi lennék, azt lehet valahol jelezni?
Tome
javaslatok helye
Ha a szerkesztőség felé közvetlenül tennél javaslatokat, akkor írj a info##kukac##weblabor.hu-ra.
Persze nem ígérjük, hogy teljesíteni tudjuk kívánságod, de pl. a fórum esetén lehet, h. mások is tudnak hasznos információkkal/linkekkel szolgálni.
Regex?
Mindkettő
-boogie-
RE: regex?
A következőkben lesz néhány hivatkozás már magyarul is elérhető tartalmakra, ahol a "regex" formát részesítették előnyben az angol mintájára.
Próbáld meg kimondani a "regex"-et és a "regexp"-et szövegkörnyezetben! Szvsz, az elsővel könnyebb boldogulni, mert nincs mássalhangzó-torlódás.
Nem tudom
Es, ha mar itt irok, akkor par szo a cikk-rol. Szerintem egy kicsit elnezted a celkozonseget, mert az elejen olyan dolgokrol irsz, hogy dir *.php, aztan hirtelen olyan melysegekbe esel, amit csak a beavatottak erthetnek. Egyebkent nekem tetszik a cikk.
Ja, es meg valami. Nem tudja senki, hogy hova lett a php kezikonyvbol a regex(p:) Pattern Syntax magyar forditasa? Az egy nagyon jo kis leiras volt.
Ahogy tetszik! Ízlések és
A célközönséggel kapcsolatban nem gondolom, h. tévedtem volna, de sejtem, mire gondolsz. Elsősorban azért lett ez a bevezető olyan amilyen, mert - kicsit nagyképűen - szeretnék rendet tenni a fejekben. Azt gondolom, h. van néhány közhiedelem és tévhit a regex-el kapcsolatban, amit jó lenne eloszlatni. Ezek legtöbbje a hiányos alapokból és megfelelő összefoglaló irodalom hiányából fakad, ezt a saját magamon tapasztaltam.
Ebben a konkrét cikkben nem az új technikákra helyeztem a hangsúlyt, hanem az elméleti kérdésekre tértem ki:
A cikk többszöri újraolvasása után szívesen megváltoztatnék néhány részt én is, de ez már így marad. Örülnék azonban, ha leírnád, pontosan mely részek miatt gondoltad, hogy "eltévesztettem a célközönséget"?
A PHP dokumentációval kapcsolatban pedig a doc-hu##kukac##lists.php.net címen érdeklődj! Valószínűleg egy újabb XML átalakítás lehet a háttérben, amitől a magyar, régi szerkezetű fordítás "kiesett", de ez csak egy tipp.
Jo tipp ;)