Hoi en welkom bij mijn eerste tutorial. Ik hoop dat ik een goede tutorial heb geschreven.
Ik zal niet veel zeggen, behalve dat reguliere expressies niet iets is wat je even in 10 minuten kunt leren.
Ikzelf, op dit moment, heb nog niet alles van reguliere expressies geleerd. Maar dat is genoeg
om jullie een en ander te leren. Als je iets ziet dat helemaal niet klopt, pm me maar of post een reactie.
Goed, aan de slag dan maar?
PS: Ik zal wel de engelse namen gebruiken
Met reguliere expressies kun je een tekst filtreren, fouten ontdekken en zelfs controleren of een string wel de juiste
inhoud heeft (dit is belangrijk bijvoorbeeld bij het controleren op een e-mail adres).
Metacharacters kunnen daarbij een grote hulp aanbieden. Je kunt kijken of een string begint met een hoofdletter door
de metacharacter ^ te combineren met een character class. Je gebruikt de 'hoedje' om te controleren
of iets aan de begin van een lijn staat en je gebruikt de character class om aan te geven WAT er vooraan moet staan (hoewel
je ook zonder kunt, is het in dit voorbeeld is het wel handig). Dan word de regex (afkorting voor reguliere expressies) zoiets:
^[A-Z]
Dit zal dus matchen: begin van een lijn, meteen gevolgd door een hoofdletter.
Je hebt in de vorige paragraaf gezien wat een character class kan doen. Hij kan een range aangeven.
Dit is zeer belangrijk, en handig. Als deze handige functie niet bestond zou de regex van de vorige paragraaf er zo uitzien:
^[ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Niet erg overzichtelijk he? Kijk eens goed naar hierboven; het streepje is weg! Dat is ook een metacharacter, eentje dat een
range aangeeft. Dit werkt voor letters en cijfers. [a-z] of [A-Z] of [0-9]. Merk ook op dat reguliere
expressies casesensitive zijn behalve als je die functie uitschakelt (maar dat is voor een andere keer ;-).
Het streepje werkt niet als hij helemaal vooraan staat, want dan weet de regex-engine wel dat er onmogelijk een range kan zijn.
En je hoeft niet per se bijv. a-z te gebruiken: a-f werkt ook ;-).
Als je gewoon tekens plaatst in een character class dan betekent dat niet een reeks tekens matchen, maar betekent dat:
match teken1 of teken2 of teken3 enzovoorts. Met andere woorden, er word de script operator
OR tussen elke teken in de class gezet.
[abcdefgh]
Dit ziet de regex-engine dus:
Match een a of een b of een c of een d of een
e of een g of een h.
Als je sommige metacharacters plaatst in een character class, krijgen ze een heel andere betekenis. Alsof binnen een character
class een eigen regex taaltje heerst. Een voorbeeld.
^[^a-z]
Dit is wat de regex engine verwerkt:
Match de begin van een lijn meteen gevolgd door EEN teken behalve een teken tussen de a en z
.
Zoals je ziet, betekent de 'hoedje' binnen een character class: alles matchen behalve wat er achter me staat.
Dit is ook een handige functie in de regex wereld. Je kunt met parentheses backreferences (zie §2.2.1) gebruiken.
Ook kun je aantal tekens waartussen een "OR" moet staan uitbreiden. Ook kun je lookarounds gebruiken, en nog een paar andere
dingen, maar dat valt buiten "De inleiding in Reguliere Expressies" ;-).
Laten we eens kijken naar de "OR" functionaliteit.
Omdat regex-engines niet echt kunnen raden waar je een OR tussen wilt plaatsen (want meerdere tekens zijn toegestaan), is er
een metacharater gemaakt die de OR functie mogelijk maakt in een parenthese, namelijk ⇒ |.
Deze 'pipe' kun je gebruiken om een OR te plaatsen tussen de woorden. Ik ga een voorbeeld gebruiken die ik op een website heb
gelezen, maar weet niet of ik de site mag opnoemen. Dus als iemand ripper gaat roepen dan weet die alvast dat ik dit niet
geript heb, maar geleend om jullie iets te leren :).
Er bestaan verschillende mogelijkheden om regular expressions te schrijven. Zo is er regex, regular expressions, regexes. Er zijn
misschien meer, maar dan word het wat onoverzichtelijk in de regex om het te begrijpen. Wij willen ze allemaal kunnen matchen.
We maken dan gebruik van de 'pipe' en parentheses en een functie in php die ik later pas zal uitleggen (in §2.4).
De regex word dan (ik ga ervan uit dat case-sensitivity uit staat):
reg(ex(es)?|ular expressions)
Dit is wat de regex-engine verwerkt:
Match reg meteen gevolgd door ófex(met optioneel es erachter) óf
ular expressions
Zie je dat de regex-engine niet leest: reg gevolgd door een e of een x
enzovoorts.
Zie je dat hij de hele woorden neemt en niet, zoals in een character class, ze in stukjes hakt.
Je kunt met parentheses ook back references maken. Een back reference is een verwijzing naar een parenthese. Ze worden
genummerd van links naar rechts. Dus de eerste parenthese krijgt referentie nummer 1 en de 2e krijgt dus referentie
nummer 2 enzovoorts. Ik dacht ergens gelezen te hebben dat er een limiet op referenties zijn, maar daar hoef jij je geen zorgen
over te maken of je moet een zeer lange regex hebben met heel veel parentheses. Ik kan uitleggen hoe een backreferentie eruit
ziet, maar een voorbeeld is wel zo praktisch. Een voorbeeld dat veel mensen kennen die met een template parser hebben gewerkt.
Ik gebruikte deze regex vroeger om gegevens uit een TPL block te halen:
Zoals je ziet, gebruik ik in plaats van (.*?) nu 1. Een backreference kun je met
slash () of dollar teken($) + referentie nummer aanroepen. Zo word je regex korter, en hoef je geen zorgen te maken
dat je een tikfout hebt gemaakt bij de 2e parenthese.
Wil je van een parenthese geen backreference maken, dan moet je '?:' net achter de '('
zetten.
Wat bedoel ik met "Begin en Eind", vraag je jezelf zeker af ? Ik bedoel daarmee: metacharacters die de begin en eind van een
line kunnen matchen. Wat heb je hieraan, vraag je nogmaals ? Nou, dit is belangrijk bij bijvoorbeeld een e-mail adres
controleren bij het registreren. Je wilt niet dat in de inputveld een hele boek word gezet (hoewel dit toch niet kan :p) en
erachter de email adres. Want zonder de metacharacters van deze paragraaf, is dat mogelijk!
Ik heb het over het 'hoedje' ⇒ ^ EN de 'dollar teken' ⇒ $.
Het hoedje en de dollar teken matchen, respectievelijk, begin en eind.
^[A-Z][a-z]+.$
En de regex-engine leest het zo:
Match begin van lijn direct gevolgd door een teken tussen a en z (minstens 1x, meerdere
tekens zijn optioneel) direct gevolgd door een punt direct gevolgd door einde van lijn
Deze simpele regex matcht dus een zin (hoewel er op een lijn meerdere zinnen kunnen voorkomen, leestekens buiten beschouwing
zijn gehouden enzovoorts. Dit is voldoende voor de beginner, anders wordt het te moeilijk. ;-)).
Welkom bij een makkelijke gedeelte van de 'regex-taal'. Quantifiers betekent letterlijk: "hoeveelheidsbepaler". Dit is een
redelijke beschrijving van deze functie in reguliere expressies. Ze bepalen hoeveel van iets moet komen. Je kunt kiezen om de
functies * + ? te gebruiken of een ander functie te gebruiken (de accolades). Ik begin met de */+/?, dan kom
ik wel terug op de accolades.
*
teken voor mij 0 of meerdere keren matchen
+
teken voor mij 1 of meerdere keren matchen
?
teken voor mij 0 of 1 keer matchen oftewel: teken voor mij is optioneel
Je kunt de hierboven genoemde quantifiers ook vervangen door de functies { en } te
gebruiken:
*
{0,}
+
{1,}
?
{0,1}
De */+/? quantifiers staan vast, maar de cijfers tussen de accolades niet. Regel voor accolades is dus:
{min,max}
Laat je de min of max weg (allebei kan niet!), dan komt oneindig in de plaats ervan.
Een tutorial is niet compleet zonder een paar oefeningen, vind ik tenminste :) Dus hieronder heb ik een paar (moeilijke[?])
oefeningen neergezet voor je. Als het je gelukt is om er eentje goed te hebben, mail ze dan naar mij. Ik wil wel zien wat mijn
"leerlingen" van deze tutorial hebben geleerd :)
1. Maak een email regex (en niet stiekem in de script library bekijken)
2. Maak een regex dat een HTML pagina kan matchen, die valid is. Dus bij niet valid pagina's => geen match
3. Maak een regex dat internet adressen matcht.
4. Van een boek: Maak een regex dat de tijd kan matchen (hh:mm) en hij moet uur in 2 modes matchen: 12 (met am/pm dus) en 24 (dus t/m 00:00 (12 uur 's nachts)).
We zijn aangekomen bij de afscheid. Maar we zeggen geen vaarwel, maar tot ziens. Er is nog veel te vertellen over reguliere
expressies die ik nog niet heb besproken in deze tutorial. Er zal binnenkort nog een tutorial verschijnen van mijn hand, reken
daar maar op ;-). En antwoorden op de vragen hierboven kun je mailen naar mij via mijn profiel :) (ik heb het niet zo op spam
mail, anders zette ik me mail hier neer).