Reguláris kifejezések
Reguláris kifejezések
Miután a műsorújságban egy kedvelt osztrák-német krimisorozat címét Regex felügyelőnek olvastam, gondoltam ideje egy ilyen írást készíteni. Vagy lehet, hogy el kéne vándorolnom a gépek közeléből pár hétre. A bejegyzésben továbbiakban a perl-kompatibilis reguláris kifejezésekről lesz szó, amit például PHP-ben a preg_ kezdetű függvényekben használhatunk, mint mintaillesztési kifejezést.
Az alapok
Kezdjük talán ott, hogy milyen különleges karakterek adják meg a nyelv lényegét, és melyikek mikre valók:
- .
- A pont az a karakter, ami esetünkben bármely más karakterre illeszkedik, tehát ha van egy olyan kifejezésünk, hogy
/a.b/
, az azt fejezi ki, hogy az "a" és a "b" karakter között bármilyen másik karakter szerepelhet, az új sor karaktereket kivéve (például aab, a b, a=b, a*b, axb). - ?
- A kérdőjel jelzi, hogy az előtte lévő mintacsoportból nulla vagy egy szerepel a kifejezésben. A csoportokat a kerek zárójel segítségével tudjuk megadni. Például:
/aa?b/
illeszkedik ab-re és aab-re is. Az/a(aa)?b/
pedig aaab-re és ab-re. - *
- A csillag karakter működésében hasonló, mit a kérdőjel (az előtte lévő mintacsoportra van hatással), az utóbbival ellentétben viszont a jelentése "nulla vagy bármennyi". A példák mezejére áttérve tehát valami ilyesmi kifejezés:
/aa*b/
ilyesmikre illeszkedik: ab, aab, aaab, aaaab, stb. - +
- A szükséges pussz az előző két metakarakterhez, működésében ugyanolyan, hatásában pedig a "legalább egy, maximum bármennyi" kifejezéssel illethető. Lássunk valami kombináltabb példát:
/a.+b/
. A minta bármire illeszkedik aminek "a" az eleje és "b" a vége, köztük pedig legalább egy bármilyen karakter van. Például: dsax dbce, a b, a szép hosszú példa aminek a vége b. - { és }
- Ővele egy intervallumot lehet meghatározni három féle módon: {n} - pontosan n darab az előtte álló kifejezésből. {n,} - legalább n darab az előtte álló kifejezésből. {n,m} - legalább n, de maximum m darab az előtte álló kifejezésből. A ? tehát kiváltható a {0,1}-el, a * a {0,}-el, a + pedig az {1,}-el.
- ^ és $
- Ez a kettő annyira összeillik, hogy nem is láttam értelmét a szétválasztásnak. Az előbbi a szöveg elejére, míg a másik a szöveg végére illeszkedik, vagyis a
/^a.+b$/
ugyanazokra a sorokra fog illeszkedni, mint az előző példában megadott kifejezés, azzal a különbséggel, hogy itt már meg van kötve, hogy a sornak "a" betűvel kell kezdődnie és "b" betűvel kell végződnie. Előzőben nem volt ez megkötve, elég volt ha a sorban volt egy olyan rész, ahol "a" és "b" karakterek között volt legalább egy karakter (illeszkedő sorok első példája). - |
- Ez olyan "vagy" szerű dolog, példából egyszerűbben meg lehet majd érteni, mint abból, ha itt én elkezdenék magyarázkodni bárhogy is, tehát lássuk: a
/^a(c|d)b$/
illeszkedik a következőkre: acb, adb. - ( és )
- Mint azt pár bekezdéssel feljebb említettem, ezzel lehet a kifejezésben szereplő részkifejezéseket csoportosítani, amikre a későbbiekben akár hivatkozni is lehet (ennek módja függ a választott nyelvtől, például Perlben a \1, \2, \3, stb. hivatkozások vannak, míg a PHP preg_replace függvényében a $1, $2, $3, stb. használható).
- [ és ]
- A szögletes zárójelek között egy karakterekből álló sorozatot adhatunk meg, vagy egy intervallumot, és a kifejezés az ebben benne lévő karakterekre fog illeszkedni. Itt sem maradhatunk példa nélkül: a
/a[cd]b/
ugyanazt eredményezi, mint a/a(c|d)b/
. A/a[0-9]/
illeszkedik egy olyan részre, ahol egy "a" betűt egy darab számjegy követ. Ezt a típust invertálhatjuk is a ^ karakter nyitózárójel utáni közvetlen alkalmazásával: a/a[^cd]b/
tehát azt fejezi ki, hogy az "a" és a "b" között van egy olyan karakter, ami nem "c" és nem is "d" (akár új sor karakter is lehet).
Kifejezés módosítók
A kifejezés maga tehát valami ilyen forma dolog /itt van valami izé/x
, ahol az a bizonyos x a kifejezés módosítók sora, amik a következők lehetnek:
- i
- A legegyszerűbb, leggyakrabban használt módosító, ami a RegEx alapértelmezett kis- és nagybetű érzékenységét kapcsolja ki ("magyarul" case insensitive lesz :)). Kérdéses mondjuk, hogy egy ilyen módosító a kifejezés kiértékelését mennyire lassítja le, de ez már egy más kérdés.
- m
- Egy hasznos módosító, ami a ^ és $ metakarakterek viselkedését változtatja meg. Első körben tudni kell azt, hogy a Perl RegEx a kapott szöveget egy sorként kezeli, még akkor is, ha az több új sor karaktert is tartalmaz, így a ^ a szöveg legelejére, a $ pedig a szöveg legvégére illeszkedik. Az m módosító használatával viszont a ^ és a $ karakterek passzolni fognak a sor elejére és a sor végére is.
- s
- Ismét egy hasznos dolog, ami a . metakarakter viselkedését változtatja meg úgy, hogy az új sor karakterre is illeszkedjen.
- x
- A whitespace karakterek (tab, szóköz, újsor) teljes mértékbeni figyelmen kívül hagyása, kivéve ha [ és ] között szerepel, vagy ha megfelelően escape-elve van (PHP esetében ez teszi lehetővé a használható eszközök kiterjesztését is).
Kiterjesztett eszközök
Ezek a bizonyos kiterjesztett eszközök az amúgy értelmetlennek számító (?
karakterlánccal kezdődnek és egy )
karakterrel fejeződnek be. Ezen belül elég sok fajtájuk van és nem is vagyok teljesen biztos benne, hogy ez támogatott pl. a PCRE által is. Mindenesetre essünk neki a fontosabbak átvételének:
- (?#szöveg)
- Ezzel kommenteket tehetünk bele a reguláris kifejezésünkbe. Első látásra talán értelmetlennek tűnhet, hogy mi a fenének is akarnánk egy egy soros eléggé átláthatatlan kifejezést még telitűzdelni kommentekkel is, hogy még annyira se legyen átlátható. De ha feldereng bennünk egy korábbi bekezdés szövege, az x módosító arról is gondoskodik, hogy a kifejezésünket több sorba tördelhessük.
- (?imsx-imsx)
- Ennek segítségével a kifejezésen belül kapacsolhatjuk be vagy ki az egyes módosítókat. Hatásköre az éppen aktuális csoport vége. Pl.:
/(?i)ab/
illeszkedni fog az ab, Ab, aB, AB stringekre. - (?:kifejezés)
(?imsx-imsx:kifejezés) - Ez a változat hasonlít a szimpla csoportosításra, azzal a kivétellel, hogy a második típusában a használt módosítókat is be lehet állítani, ezen kívül ezekre a csoportokra nem lehet a későbbiekben a \1, \2, stb. (vagy $1, $2, stb.) cimkékkel hivatkozni.
- (?=kifejezés)
- Ez egy érdekes eszköz, amit hosszadalmas magyartalan magyarázkodás helyett inkább egy egyszerű példával tudnék jellemezni:
/[a-z]+(?=\t)/
illeszkedik egy olyan - csak kisbetűket tartalmazó - karaktersorozatra, melyet egy TAB karakter követ, viszont a TAB karakter így nem kerül bele a találatba (pl.: preg_match esetében a $matches tömbbe). - (?!kifejezés)
- A kifejezést lehet vele egyszerűen negálni.
Végezetül még ajánlanék egy hasznos kis segédprogramot, amivel gyakorolhatjuk a reguláris kifejezések írását. Ez a program nem más, mint a The Regex Coach, ami esetenként eléggé meg tudja könnyíteni az ember életét, főleg nagyobb lélegzetvételű kifejezések esetén. További kellemes mintaillesztgetést!