Eszközök |
Szabályos kifejezés
A szabályos kifejezés (angolul regular expression) egy olyan, bizonyos szintaktikai szabályok szerint leírt string, amivel meghatározható stringek egy halmaza. Nevének rövidítésére gyakran a regexp vagy regex kifejezés használatos. A szabályos kifejezéseket sok szövegszerkesztő, illetve segédprogram használja, főleg szövegek keresésekor vagy szövegek bizonyos minták szerinti kezelésekor. A szabályos kifejezéseket a jelsorozatokkal, stringekkel való műveleteknél több programozási nyelv is használja, illetve támogatja. Például a Perl és a Tcl is rendelekezik direkt szabályos kifejezések elemzésére szolgáló szintaktikai elemzővel. A különböző Unix disztribúciókban lévő segédprogramok jelentős része (beleértve a sed szövegszerkesztőt és a grep szűrőt) támogatta először a szabályos kifejezések használatát.
[szerkesztés] AlapkoncepcióA gyakran mintának nevezett szabályos kifejezés a jelsorozatok, stringek egy halmazát határozza meg. A minták használatával tömören megadhatók halmazok leírásai annélkül, hogy az összes elemüket fel kellene sorolni. Tegyük fel például, hogy egy halmaz a következő jelsorozatokat tartalmazza: Handel, Händel, és Haendel. Leírhatók-e a halmaz elemei a „H(ä|ae?)ndel” mintával (más szavakkal: mondhatjuk-e, hogy a mintához mindhárom string illeszkedik?). Amint az a későbbiekből kiderül, általában azonos halmazok különböző mintákkal is leírhatóak. A legtöbb formalizálásnál a következő operátorok használatával konstruálhatók meg a megfelelő szabályos kifejezések.
A fenti konstrukciók egymással kombinálva a különféle formák komplex ellenőrzését teszik lehetővé. Tehát a „H(ae?|ä)ndel” és a „H(a|ae|ä)ndel” érvényes, szabályos minták, és ezen túlmenően, mindkettőhöz illeszkednek a előző példaként megadott jelsorozatok. Másik példa a előzőekben leírt operátorok kínálta lehetőségek kihasználásra: Legyen a minta a következő: „(((nagy(( )*)*)*((nagy(( )*)*)?(apa|anya)”. A mintához illeszkedik a következő stringek közül bármelyik: apa, anya, nagy apa, nagy anya, nagy nagy apa, nagy nagy anya, nagyapa, nagyanya, nagy nagyapa, nagy nagyanya, nagy nagy nagyapa, nagy nagy nagyanya és így tovább. A szabályos kifejezések pontos szintaxisa a változó eszközök és alkalmazások miatt egységesen nem adható meg; a további részleteket lásd a Szintaxis résznél. [szerkesztés] TörténetA szabályos kifejezések először az automata elmélet és formális nyelvek elmélete (mindkettő része a elméleti számítógép tudománynak) kapcsán merültek fel. Ezek az elméletek a számítógép működésének modellezésénél (automaták), illetve ezek osztályozásánál és leírásásnál formális nyelvek voltak fontosak. Az 1940-es években Warren McCulloch és Walter Pitts az idegrendszer neuronokkal történő modellezésének leírásához használt egy kicsiny, egyszerű automatát. A matematikus Stephen Kleene ugyanezt a modellt matematikai jelölésekkel, az úgynevezett szabályos halmazok alkalmazásával írta le. Ken Thompson ezt a jelölési módot építette be az általa készített QED szövegszerkesztő programba, és ez került a Unix szerkesztőjébe ed, ami a grep elkészüléséhez vezetett, amely szabályos kifejezéseket használ. Azóta, a szabályos kifejezések széleskörben elterjedtek a Unix és a Unix-szerű rendszerek segédprogramjainál, mint például az expr, az awk, az Emacs, a vi, a lex, és a Perl. A Perl és a Tcl szabályos kifejezései a Henry Spencer által írt regex-ből származnak. Philip Hazel kifejleszti pcre a (Perl Compatible Regular Expressions) alkalmazást, amely képes szimulálni a Perl szabályos kifejezési funkcionalitásait, és több modern eszközben is megjelenik, többek között a PHP-ben, és az Apache-ban. A későbbiekben felsorolt nagyszámú eszköz, könyvtár stb. bizonyítja, hogy a szabályos kifejezések ma egyre nagyobb teret nyernek, és egyre több olyan eszköz jelenik meg, amelyeket direkt ezek kezelésére fejlesztettek. A szabályos kifejezések számítógépes nyelvekbe integrálása még nagyon kevéssé elterjedt, bár a Perl szabályos kifejezés integrációja a legjobbak között van, de a jövőben a fejlesztésnél még erőfeszítések szükségesek (angol nyelven Perl6) az integráció növeléséhez. Ide tartozik a Apocalypse 5 projekt is. [szerkesztés] Kapcsolat a formális nyelvek elméletévelA szabályos kifejezések konstansokból és operátorokból épülnek fel, stringek egy bizonyos halmazát jelölik, illetve műveleteket definiálnak a halmaz elemeire, vagy az elemei között. Adott véges Σ ábécé a következő konstansokat
és a következő operátorokat definiálja:
Több leírásban a A felesleges zárójelezések elkerülésére a műveletek eltérő prioritásúak: megegyezés szerint a Kleene csillag nagyobb prioritású, mint a konkatenáció és az unió, így elhagyhatók a zárójelek, amennyiben az nem okoz kétértelműséget. Például, az (ab)c abc-nek is írható és az a|(b(c*))-t írhatjuk mint a|bc*. Példák:
A szabályos kifejezések formális definíciói szándékosan a lehető legegyszerűbbek és igyekszenek elkerülni a redundáns mennyiségi jelölőket, (? és +) amelyek kifejezhetőek a következőképpen: a+= aa*, és a? = (ε|a). Néha a komplementer képző operátort ~ is használják (~R jelöli az összes string halmazát a Σ ábécé felett, amelyek nem részei R-nek. A komplementer képző operátor redundáns: minden esetben kifejezhető egyéb operátorok használatával. A szabályos kifejezések ebben az értelemben pontosan kifejezhetők a nyelvek azon csoportjával, amelyeket véges automata el tud fogadni: a szabályos nyelvekkel. Némi ellentmondást jelent, hogy a szabályos nyelvek néhány osztálya leírható egy automatával, azonban a szabályos kifejezések hossza exponenciálisan növekvő , míg más esetben a hossz növekedése csak lineáris. A szabályos kifejezések megfelelnek a Chomsky féle hierarchia type 3 nyelvtanoknak, és leírásukhoz a szabályos nyelvek használhatók. Amint azt a példák is mutatják, különböző szabályos kifejezések azonos nyelven kifejezhetők: a használt formalizmus redundáns. Lehetséges olyan algoritmus írása, amelyik két adott szabályos kifejezésről eldönti, hogy az adott nyelven egymásnak megfelelnek-e, ekkor a kifejezéseket egy minimális determinisztikus automatává redukálják, így eldönthető, hogy azok izomorfak-e (ekvivalensek). Hogyan lehet megszüntetni a redundanciát? Található-e szabályos kifejezések olyan megfelelő részhalmaza, amely még teljesen kifejező? A Kleene csillag és halmazok uniója nyilvánvalóan szükségesek, de korlátozottan használhatóak. Kiderül, hogy ez a probléma meglepően bonyolult. Egy egyszerű szabályos kifejezésről kiderül, hogy nincs szisztematikus helyettestési eljárás valamilyen normál formává alakítására. A szabályos kifejezések végesen nem axiomatizálhatók. Más módszert kell igénybe venni. A fentiek miatt merült fel a csillag súly probléma . A gyakorlatban megvalósított „szabályos kifejezés elemző gépek” működését nem lehet egy szabályos kifejezés algebrával leírni; a részleteket lásd a alul pontban. [szerkesztés] Szintaxis[szerkesztés] „Tradicionális” Unix szabályos kifejezésekAz „alap” Unix szabályos kifejezéseinek definíciói a POSIX megjelenésével ugyan elavultak, ennek ellenére széles körben használatosak a visszafelé kompatibilitás miatt. A legtöbb szabályos-kifejezés–felismerő Unix (segéd)program, például a grep és a sed alapértelmezésként használja még ezeket. Ebben a szintaxisban a legtöbb karaktert literalként kezelik – saját magukhoz illeszkednek csak („a” összeillik „a”-val, „(bc” összeillik „(bc”-hez stb.). A kivételek az illesztő-karaktereknek nevezett metakarakterek:
Meg kell jegyezni, hogy bizonyos szabályos kifejezés elemző megvalósítások a \ metakaraktert másként értelmezik, mint azt az előzőekben mutattuk. A grep régi változatai nem támogatják a választási operátort jelölő „|” karakter használatát. Példák:
A különféle beállításoktól, értelmezésektől függően változhat a karakterek sorrend szerinti csoportosítása (pl. bizonyos beállítások szerint a karakterek sorrendje az abc..yzABC..YZ szerinti, míg más elv szerint az aAbBcC..yYzZ a sorrend) a – nem mindenki által elfogadott – POSIX szabvány meghatározza a karakterek osztályokba vagy csoportokba sorolást a következő tábla alapján:
Például: [[:upper:]ab] meghatározza az összes nagybetűt és a kisbetűs 'a'-t és 'b'-t. (Egy ASCII kódtáblán szinesben mutatja a különböző POSIX osztályokat a következő link http://www.billposer.org/Software/RedetManual/builtincharclass.html.) Megjegyezzük, hogy a magyar ábécé ékezetes betűinek kezelése esetenként eltérő módon történik, ugyanis a különféle szabványok nem minden esetben térnek ki a különböző országokban használatos, az angol ábécé betűitől eltérő betűk kódolására, kezelésére. Gyakori probléma például, hogy az ábécé szerinti rendezésnél az ékezetes betűk rossz helyre kerülnek, mivel a karakterek kódjai – többnyire – a nem ékezetes karakterek kódjai után következnek, tehát rendezés szempontjából azok valóban „nincsenek sorban”. [szerkesztés] POSIX modern (bővített) szabályos kifejezésekTöbb modern, „bővített” szabályos kifejezést használhatnak a modern Unix segédprogramok a parancs sorban az „-E” jelző hatására. A POSIX bővített szabályos kifejezéseinek szintaxisa néhány kivételtől eltekintve megegyezik a „tradicionális” Unix szabályos kifejezéseive. A követekező metakaraktereket értelmezi még a program:
A „visszatörtjel” eltávolítása: \{…\} átalakul {…}-re és \(…\) átalakul (…)-re. Példák:
A speciális jelentéssel bíró '(', ')', '[', ']', '.', '*', '?', '+', '^' és '$' karakterek az escape-zés használatával literálként lesznek értelmezve. Ez azt jelenti, hogy a karatert megelőzi a '\' karakter, amely így „escape-ezve” már saját literálját jelenti csak. Példa:
[szerkesztés] Perl-kompatibilis szabályos kifejezések (PCRE)A Perl egy gazdag és átlátható szintaktikai elemzővel rendelkezik, amely még a POSIX kiterjesztett szabályos kifejezéseinek elemzésére is alkalmas. Az átláthatóságra jó példa, hogy a \ karakter minden esetben egy nem alfanumerikus karaktert jelöl. Szabályozható, hogy a specifikáció kiválasztásával milyen illesztési algoritmust használjon az elemző: a Perl szintaxis, szemben a POSIX szintaxissal a „mohó” algoritmust követi, míg a másik nem. Ez azt jelenti a gyakorlatban, hogy a /a.*b/ minta .* jelölése azt jelenti, hogy annyit illeszen az elemző, amenyit lehet, míg a /a.*?b/ mintában a .*? jelöli, hogy csak olyan kicsit illeszen az elemző, amennyit csak lehet. Ha adott a „a bad dab” string, akkor az első minta illesszkedik az egész stringhez, míg a második csak az „a b” részt „ismeri fel”. A fentiek miatt több alkalmazás és segédprogram is megvalósítja a Perl „szerű” szintaxist, többek között – a Python, Ruby, exim, és a BBEdit. Készültek olyan alkalmazások, például a Tcl, amely a POSIX előírásokat és a Perl kiterjesztéseket is megvalósítja. Ennek viszont az a következménye, hogy a Tcl illesztési modellje nem mohó, azaz a „lehető legkisebb” illesztési elvet valósítja meg. [szerkesztés] Minták a szabálytalan nyelvekbenA minták használatának lehetőségeiből származó előnyök túlmutatnak a szabályos nyelveken. Például, megjelölt alkifejezések zárójelek segítségével történő csoportosítása és azok későbbi kiértékelése, minták használatával, különösen az ismétlődő szavak esetében, („papa”, „WikiWiki” stb.) a formális nyelvek elméletében bevett gyakorlat, és a „(.*)\1” mintával igen egyszerűen kivitelezhető. Ugyanakkor, a fenti megoldás nem megengedett a nem szabályos és környezetfüggő nyelvek esetében. A mintaillesztést korlátlan számú előzetes referencia használatával számos a modern NP-teljes komplexitású eszköz biztosítja. Mégis, egyre több eszköz, könyvtár, elemző támogatja a szabályos kifejezések és a mintaillesztési mechanizmus használatát. Ennek az a következménye, hogy a „szabályos kifejezés” és a hozzá tartozó mintaillesztés jelentését eltérően értelmezi a formális nyelvek elmélete. [szerkesztés] Megvalósítások és futási időkLegalább két, alapjában eltérő algoritmus típus ismert, amely eldönti, hogy (és hogyan) illeszkedik-e egy adott string egy szabályos kifejezéshez. A régebbi és gyorsabb megoldás alapja, hogy a formális nyelvek elmélete szerinti megengedett, hogy minden nemdeterminisztikus véges állapotú gép (Nondetermistic Finite state machine, NFA) átalakítható egy determinisztikus véges állapotú géppé (Deterministic Finite state machine, DFA). Az algoritmus ezt az átalakítást hajtja végre vagy szimulálja, és ezután az edményül kapott DFA elemzi a bejövő jelsorozatot. Egészen pontosan, egy n hosszúságú bejövő string ellenőrzése egy M hosszúságú kifejezés esetén O(n+2m) vagy O(nm) időt igényel, a megvalósítástól függően. Erre az algoritmusra gyakran mint DFA-ra hivatkoznak. Ez valóban gyors, azonban csak és kizárólag illeszkedést lehet vele vizsgálni, és nem képes emlékezni csoportosított alkifejezésekre. Létezik olyan változata az algoritmusnak, amely képes a emlékezni csoportosított alkifejezésekre, azonban ekkor a futási idő O(n2m)-re lassul. A másik algoritmus csoport a mintaillesztés elvét használja ki, mintát illeszt a bejövő jelsororzathoz visszaléptetési módszerrel. (Ezt az algoritmust gyakran nevezik NFA-nak, azonban ez nagyon zavaró.) A futási ideje exponenciálisan növekvő, ha azt az egyszerű megvalósítást vizsgáljuk, amikor az „(a|aa)*b” mintát kell illeszteni egy kifejezéshez, ami választást és kötetlen számú illeszkedés is magába foglal, a lehetséges alapesetek száma igen nagyra nőhet – a kifejezés hosszától és összetettségétől függően, és ezek számától függően exponenciálisan nő a futási idő. Nagyon komplex megvalósítás esetén is csak a legegyszerűbb és leggyakoribb eseteknél nőhet a sebesség valamennyit, de egyébként csökken. Még a visszaléptetés megvalósítása is csak egy exponenciális korlátot jelent, a legrosszabb esetre, viszont nagyobb flexibilitást biztosít. Néhány esetben úgy növelik a teljesítményt, hogy a két algoritmust egyszerre alkalmazzák: első lépéseben a gyors DFA illesztő algoritmussal végigmennek a bejövő stringen, majd a nem illeszthető részekre a sokkal lassabb visszaléptető eljárást használják. [szerkesztés] Lásd még[szerkesztés] Angol nyelvű irodalom
[szerkesztés] Külső kapcsolatok
[szerkesztés] Angol nyelvű cikkek
[szerkesztés] Eszközök
[szerkesztés] Programkönyvtárak
|