Reguláris kifejezés: Gyorstalpaló

A reguláris kifejezés nem mondanám, hogy fiatal. Nagyon kevés ember ismeri őket és még kevesebb úgy igazán. Gyakran előfordul, hogy megkeresnek ennek kapcsán emberek. Itt most nem 1-2 emberről van szó. A postafiókomban rákerstem a reguláris kifejezésre, ahol nem én vagyok a küldő és az elmúlt 6 hónapban 84 levelem van. Ez mind magyar levél és igen nagy százalékban kérdés.

Mire is való a reguláris kifejezés?

Alapvetően mindenre. Elfogadott tényként kezelik, hogy ez a programozóknak kell. Ez nem igaz, mert sok más területen hasznos tud lenni. Google táblázatkezelőjében, Google Analytics-ben, Nodepad++ és egyéb szerkesztőkben, ha egy hosszabb szövegen kell korrigálni dolgokat. Persze engem kicsit elkeserít, amikor egy elvileg régebb óta programozó egyénnek fogalma sincs róla, hogy mi az és a zárójel neki újdonság.

Nézzünk pár példát. Kezdjük egy Analytics esettel. Vannak ugyebár az oldalmegtekintések. Erre láthattunk példát egy korábbi cikkben is, de most boncolgassuk kicsit és fektessük erre a hangsúlyt. Jelen esetben az URL nézzen ki úgy egy átlagos cikkhez, hogy /blog/{év}/{hónap}/{slug-avagy-seo-url}/. Hogyan kérjük le mondjuk a tavalyi publikált cikkek idei első három hónapra eső látogatottságát? Ha például van egy cikk, ami tavaly nyáron lett publikálva és most januárban is erősen látogatottnak számít, akkor biza az egy érdekes téma még ma is az embereknek és mivel régen írtál róla, lehet írni kellene újra.

Alap illeszkedés

A reguláris kifejezést többnyire valamilyen konténerbe kell rakni, de van ahol nem kell, mert az adott alkalmazás nekünk ezt megcsinálja. Én jelenleg a reguláris kifejezések konténerének az általánosan elfogadott / (per) jelet fogom használni. Ez annyit jelent, hogy minden reguláris kifejezés, amit írok /itt a reguláris kifejezés/ formában fog kinézni. Ami ebben a konténerben van az kell, hogy illeszkedjen valahol. A legegyszerűbb eset, amikor egy sima szövegrészt keresünk. Legyen a szöveg amiben keresünk:

0

A reguláris kifejezésünk pedig: /20/. Ez azt jelenti, hogy keressük azokat a sorokat, amik tartalmazzák valahol azt, hogy 20. Ez jelen esetben ezekre illeszkedik:

1

Vagyis minden. Minden sorban ott van a 20. 2014-el kezdődik minden sor. Mi most célozzuk meg az összes olyan cikket, ami 20-án lett publikálva. Ehhez fel tudjuk használni a mellett lévő / jeleket. Logikusan írhatnánk, hogy //20//, de ez nem működne. A / konténereken belül nem írhatunk ilyen jeleket, mert akkor azt hiszi a feldolgozó, hogy ott vége van a kifejezésnek. Ilyen esetben le kell úgymond védeni a karaktert. Ezt később is használni fogjuk más dolgokra. Levédeni a karakterrel lehet. Ez azt jelenti, hogy az utána álló karaktert értelmezze sima szövegként (aki ért hozzá ne verjen meg, próbálom egyszerűen fogalmazni). Ezek után így fog kinézni a: //20//. Ez annyit tesz, hogy van egy per jel, ami a konténer kezdete, majd egy levédett perjel, ami a miatt jelzi, hogy nem a reguláris kifejezés vége. Ezek után jön a 20, amit keresünk, újabb levédett perjel, majd a konténerzáró. Sok az össze-vissza perjel mi? Nembaj szokj hozzá 🙂 Most akkor ezt kerestük, amiben megtalálható a /20/ szövegrész.

2

Ennek a keresésnek amúgy nem sok haszna van, de a per jelet el tudtam vele magyarázni, remélem érthetően.

Sor eleje, sor vége

A reguláris kifejezés alapvetően sorról-sorra értendő. A fentebb leírt problémakörben minden URL egy sor. Minden sornak van egy eleje és egy vége. Ez magától értetődő, de nagyon hasznos dolog. Erre a két elemre a ^ (kalap) és a $ (dollárjel) használatával lehet hivatkozni. Ha most arra keresnénk rá, hogy mi az amit a 2000-es években publikáltunk, akkor már a tanultak alapján //2/ lenne. Mert ugye mi azt szeretnénk, ha lenne ott egy / aztán pedig hogy 2 és valami. Például 2000, 2001, 2014 vagy 2453. A /2 viszont minden máshol is szerepelhet. A nap is kezdődhet /2-vel és akár a slug is. A hónap szerencsére nem. Erre jó nekünk a sor eleje és vége jel. Meg tudjuk vele mondani, hogy az adott kifejezés a sor elején helyezkedjen el. Tehát az általunk keresett kifejezés: /^/2/. Ebben az esetben csak az fog rá illeszkedni (ok a fenti felsorolásból mindegyik), ami egy per karakterrel kezdődik, egy 2-essel folytatódik. Az összes többi rész meg minket nem érdekel. Hasznos lehet, ha például egy adott végződésre keresel, mint mondjuk a html, ami akárhol lehet a szövegben, de nekünk csak az érdekes, aminek a végén van hogy html. Hogyan néz ki akkor ez? /html$/. Vagyis illeszkedjen arra, hogy html-sorvég, tehát a html szövegrész után ne legyen már semmi csak a sor vége.

Jóker karakter

Van egy (több is) veszélyes karakter, aminek sokan áldozatul esnek. Ez pedig a . (pont), ami azt jelenti, hogy bármilyen karakter. A fentebbi html végződésnél tehát, ha azt írjuk, hogy /.html$/ az nem azt fogja jelenteni, hogy pont-html-sorvége, hanem hogy bármilyenkarakter-html-sorvége. Mit jelent ez?

  • [akármi]ahtml[sorvége]
  • [akármi]bhtml[sorvége]
  • [akármi]xhtml[sorvége]
  • [akármi]4html[sorvége]
  • [akármi]6html[sorvége]
  • [akármi].html[sorvége]
  • [akármi]%html[sorvége]

A fenti felsorolásban, minden sorra igaz. Ha mi mégis a pontra szeretnénk keresni, akkor azt le kell védeni, ahogyan tanultuk fentebb: /.html$/. Így mostmár csak azok illeszkednek rá, ahol egy pont van, majd a html szöveg és egy sorvége.

Felsorolás és ismétlődés

Gyakran előfordul, hogy olyan szövegrészt keresünk, amiről tudjuk, hogy milyen formája van, de nem fix a tartalma. Eddig tulajdonképpen egy egyszerű keresés funkció is megfelelt volna. Mi van akkor ha egy szövegben mondjuk árakat akarunk keresni? Tudjuk, hogy az árak ilyen formában találhatóak meg a szövegben: 123123huf vagy 25530ft. Itt ugye a lényeg, hogy nem fix az összeg. Persze itt is rá lehet keresni a huf és a Ft kifejezésekre, de mi a ft például sok más szóban is szerepelhet például a szaft. Persze lehetne a szöveget jobban megírni és szóközzel elválsztani meg hasonló, de nem így van. Van az úgynevezett felsorolás, ami nekünk most segíteni fog. Ha valamit szögletes zárójelbe rakunk, akkor azt nem úgy szövegként fogja keresni, hanem a zárójelben lévő karakterek valamelyikét fogja jelenteni. Ennek a fromája: [abc]. Ez nem azt jelenti, hogy abc szerepeljen a szövegben, hanem hogy a vagy b vagy c. Itt megadhatunk tartományt is, mint például [a-z], [0-9], [A-Z] vagy akár össze is vonhatóak [a-z0-9] alakban. Utóbbi azt jelenti, hogy egy karakter, amit bármilyen betű a és z között vagy bármilyen szám 0 és 9 között. Mint látható a - jelnek külön szerepe van. Ha ilyet szeretnénk tennk egy felsorolásba, akkor vagy le kell védeni vagy a felsorolás végére rakni. Ezek alapján [a-c] nem azt jelenti, hogy a, - vagy c, hanem a és c közötti betűk (a, b, c). Ha mi ezt szeretnénk, akkor célszerű így írni: [ac-] vagy [a-c]. Még jobb, ha keverjük és a végére írva levédjük. Végére azért írjuk, hogy később ha ránézünk tudjuk mi is, hogy ez nem tartomány, levédeni pedig azért, hogy ha valaki beszúr utána valamit, akkor ne váljon tartománnyá: [ac-]. Ha szögletes zárójelre akarsz illeszteni, akkor le kell védeni. Tehát ha keresel olyan szöveget, ami szögleteszárójel-bármilyenbetű-szögleteszárójelzár, akkor /[[a-z]]/. Van még egy különleges karakter itt, amit már használtunk, de mást jelent. Ez nem más mint a ^. Ha egy felsorolás kalappal kezdődik, akkor tagadja azt. Tehát bármi, ami nem a felsoroltak. Például, ha keresünk egy karaktert, ami nem szám: /[^0-9]/

Ez önmagában nem elég a problémánk megoldására. Ez többször szerepelhet, ráadásul nem tudjuk hányszor. Meg lehet mondani, hogy adott karakter/blokk (a blokkról később) ismétlődő-e. Erre ha nagyon szétbontjuk 6 lehetőségünk van. Vagyül sorra őket (ha bármelyikre szeretnénk illeszteni, akkor logikusan le kell védeni).

Ismétlődés akár nullaszor

Ez arra van, hogy ott van valami akárhányszor (akár 100x is), de akár nullaszor is, tehát nincs ott. Ezt a * karakterrel lehet. Ez gyakran van használva a . jóker karakterrel. Mondjuk, hogy van egy csomó termékünk. Sapkák és sálak. Minden termék neve a színével kezdődik, majd egy jelzőt kaphat (ez nem biztos, hogy van neki) és végül a típusa.

3

Szeretnénk minden zöld színű sapkát. Itt ugye már nehezebb a kérdés. Minden sorban két szónak kell egyeznie. Itt jön az ismétlés és a már említett jóket karakter.

  1. Illeszkednie kell a Zöld szóra a sor elején: /^Zöld/. Eddig ok.
  2. Jön egy szókör, mert a Zöldeskék nem jó nekünk: /^Zöld /.
  3. Itt jöhet bármilyen karakter, akárhányszor. Akár nullaszor is: /^Zöld .*/. Ez így most minden Zöldre illeszkedik, mert ugye a Zöld után szóköz aztán jöhet bármi. Így akár oda se kellett volna írni a .*-ot.
  4. Sapka következik. Zöld[szóköz][bármilyen karakter bárhányszor]Sapka[sorvége]. A sorvég azért kell, hogy biztosan sapka legyen és a Sapkakötő ne illeszkedjen: /^Zöld .*Sapka$/

Mi illeszkedik erre?

4

Ezt jelenti az ismétlődés, de akár nullaszor is.

Ismétlődés, de legalább egyszer

Ez majdnem ugyan az, mint a *, de itt egyszer legalább szerepelnie kell. Lehet többször is, de nullaszor nem. Ezt a + jellel lehet elérni. Keressük ki azokat, amik zöldek, sapkák és van jelzője is.

  1. Zöld a sor elején: /^Zöld /
  2. Jöjjön utána valami, de legalább egy karakter: /^Zöld .+ /
  3. Legyen a bármi legalább egyszer után egy szóköz, majd a Sapja kifejezés és egy sorvége: /^Zöld .+ Sapka$/

Mi illeszkedik erre?

5

Láthatóan a jelző nélküli sapja nem illeszkedik, mert a szóköz után itt nullaszor ismétlődik a bármi, azaz nincs semmi a Zöld és a Sapka között.

Ismétlődés legallább N alkalommal

Van még egy zárójeles kifejezés (pontosabban ezzel együtt még kettő, de a másikről később). Ez nem más, mint a kapcsod zárójelek: { }. Ide számokat kell írni. Három alapja van:

  1. N-szer ismétlődik: [abc]{4}. Ez azt jelenti, hogy négy egymás utáni a vagy b vagy c. Nem 4 a, hanem 4x ezek közül valamelyik. Mi illeszkedik erre?
    • aaaa
    • aaab
    • aaba
    • acab
  2. Legalább N-szer ismétlődik: [abc]{4,}. Ez hasonló, mint a fenti, annyi az eltrés, hogy 4x vagy többször.
    • aaaa
    • aaab
    • aabac
    • acabcbabbbabcbcacabacbacbabbbabcabc
  3. Legalább N-szer, de maximum M-szer ismétlődik: [abc]{4,7}. Ez szintén hasonló, de itt nem ismétlődhet többször, mint 7x.
    • aaaaa
    • aaab
    • aabacb
    • acabcba

Vagy van, vagy nincs

Ez kicsit kilóg a listából, mert itt ha szigorúan vesszül, akkor nem ismétlődésről van szó. Ez a ? karakter. Ha valamit ez követ, akkor az vagy van egyszer vagy nullaszor. Például ha egy url-t vizsgálunk, akkor http:// vagy https://. Ezt a legegyszerűbben így tudjuk leírni: /https?:///. Mit jelent ez? Keresünk olyan karakterláncot, ami http-vel kezdődik, majd egy s betű követheti, de az nem biztos, hogy ott van. Ezután pedig következik a kettőspont-per-per.

Térjünk vissza a forintokhoz

Ugye mi olyan adatokat keresünk, ami a fent leírt formákat keresi meg.

  1. Van legalább egy szám, de utána bármennyi és nem kell a sor elején lennie: /[0-9]+/. Ez most minden számsort kiszed nekünk.
  2. Ezután következik egy ft vagy huf: /[0-9]+(ft|huf)/.

Mi az a zárójel? És mi az a függőleges vonal? Következő fejezet 🙂

Blokkok

A reguláris kifejezésekben csinálhatunk blokkokat. A blokkokat nagyon gyakran használjuk. Főleg, ha nem csak keresésről, hanem szövegcseréről van szó. Egy blokkot a kerek zárójelekkel lehet csinálni: ( ).

Referencia

Egy blockra a N-el lehet hivatkozni. Tehát az első zárójeles részre a 1-el lehet.

6

A fenti mondjuk egy NFL pontozás. Denver és New England. Kell nekünk belőle a csalapt neve és a pontja.

  1. A sor kezdődik a Pontok:[szóköz] formában: /^Pontok: /
  2. Folytatódik valamennyi, de legalább egy nagybetűvel (pontosabban 2 vagy 3, de most ezzel ne foglalkozzunk, hátha később lesznek rövidebb vagy hosszabb rövidítések): /^Pontok: [A-Z]+/
  3. Jön egy szóközzel egy szám: /^Pontok: [A-Z]+ [0-9]+/
  4. Majd egy vs. láncot követve újabb szám és újabb legalább egy nagybetű, majd sorvége: /^Pontok: [A-Z]+ [0-9]+ vs. [0-9]+ [A-Z]+$/

Figyeljük meg, hogy a pontot levédtem. Ez még csak illeszkedik. Ha ez egy szövegcsere, akkor szeretnénk hivatkozni is dolgokra. Minden hivatkozni kívnánt részt blokkokba pakoljuk: /^Pontok: ([A-Z]+) ([0-9]+) vs. ([0-9]+) ([A-Z]+)$/

Itt most 4 blokkunk van.

  • 1: NE
  • 2: 17
  • 3: 2
  • 4: DEN

Block ismétlődés

Fentebb írtam egy példát, ahol 4x szerepelt adott karaktersorozatból karakterek: /[abc]{4}/. Ha mi azt szeretnénk, hogy ezen karakterek közül bármelyik, de adott karakter ismétlődjön, akkor létre kell hozni egy blokkot: /([abc])1{3}/. Így most azt mondtuk, hogy az egyes blokkban legyen a, b vagy c. Ez után pedig az egyes blokk tartalma ismétlődjön 3x. Így 4x egymás után. Ezze ezek fognak csak illeszkedni:

  • aaaa
  • bbbb
  • cccc

Vagy-vagy

Fentebb már használtuk. Ez a | (pipe) karakter. Tulajdonképpen karakterláncok feltételes értelmezése. Nem tudom jobban megfogalmazni, elnézést. Mire jó? A fenti forintos példában látható, de mondjuk itt egy másik: (kutya|macska|elefánt|antilop). Itt most keresünk kutya, macska, elefánt vagy antilop állatokat.

A Fentebbi színes sapkák és sálak esetén például, ha keresünk kék vagy zöld sapkákat, amiknek van jelzóje, akkor azt így tehetjük meg: /^(Zöld|Kék) .+ Sapka$/.

7

Ugyan ezzel a logikával lehet a feltételes dolgot is használni: Sapka(tartó)? tisztítás. Ebben az esetben a két egyezés lehet:

  • Sapka tisztítás
  • Sapkatartó tisztítás

Tavalyi cikkek idén?

Ugye eredetileg felvázoltuk, hogy keressük a tavalyi cikkeket, hogy idén hogyan mentek az első három hónapban. Mostmár mindent tudunk hozzá. Sokkal egyszerűbb, mint gondolná az ember: /^/2013//. Ennyi, ugye hogy nem is fáj.

Van az analytics-ben egy Content Group nevű dolog. Lehet csinálni szép dolgokat. Ott lehet igazán használni a blokkokat is. Ott úgy működik, hogy vagy a fejlesztővel beszélsz, hogy kommunikálja fel a cikk publikálási évét, vagy minden évnek megfeleltetsz egyet. Vagy és itt a lényeg: Reguláris kifejezéssel, ahol blokk van olyan néven fogja megcsinálni. Létrehozol egy Content Group-ot (erről majd egy másik cikk szólhat és az nem is a Code-ra való), majd reguláris kifejezéssel megmondod, hogy a cikkeketet évenkéntre csoportosítsa be: /^/([0-9]{4})//. Illeszkedjen tehát a sor elején egy per jel, majd egy 4 számjegyből álló szám, majd pedig egy perjel. A szám pedig legyen a blokkunk. Szép mi? Így most tudunk elsődleges dimenziónak megadni egy Content Group-ot, aminél csoportosítva látjuk évenkéntre a statisztikát. Az más kérdés, hogy az csak a beállítás pillanatától számol, tehát visszamenőleg sajnos nem lehet ráhúzni, de ez a reguláris kifejezésünktől független dolog.

Hasznos eszközök és olvasmányok

Kapcsolódó cikkek:

Balazs Nadasdi

Developer, Project Manager, Blogger, Dad... or sometihng like these

  • Amit hozzáfűznék, de így a cikkbe már nem írom bele inkább csak ide alá: A reguláris kifejezéseknél programonként eltér, hogy mit kell és mit nem kell levédeni, kell-e konténer vagy nem etc.

    Például van ahol a zárójel az sima zárójel és levédve kapjuk a blokkot. Erre tényleg nem tudok szabályt mondani, alkalmazásonként eltér.

    Ugyan így a blokkra való hivatkozás is, van ahol N van ahol pedig $N

  • eSzeL

    Lájk! 🙂 Köszönjük! 🙂