Programming and Webscripting FAQ: Regular Expressions
Met dank aan Arien, tomato en Grum
Links
- Uitleg over Greediness bij regexpen
- Korte uitleg
- Leuke naslag
- http://developer.netscape.com/docs/manuals/js/client/jsguide/regexp.htm
- drm's Regular Expressions tutorial
- http://sitescooper.org/tao_regexps.html
- http://py-howto.sourceforge.net/regex/regex.html
- http://www.perldoc.com/perl5.6/pod/perlre.html
- http://www.web-con.nl/index.php?id=114
- http://gathering.tweakers.net/forum/list_messages/161058
- http://zez.org/article/articleview/11/
- The Regex Coach
Boeken
- Mastering Regular Expressions, Second Edition
Door: Jeffrey Friedl
ISBN: 0596002890
O'Reilly & Associates Inc.
Wat zijn reguliere expressies?
Reguliere expressies zijn een manier om een (abstracte) taal te omschrijven. Je kunt zo aangeven welke woorden syntactisch correct zijn (en dus ook welke woorden dat niet zijn). Is het eerste dat in je opkomt nu "", vergeet dit dan. De onderstaande omschrijving voldoet prima.
Reguliere expressies zijn een manier om strings te omschrijven. Hoe je een bepaalde regex schrijft hangt af van de taal of tool die je gebruikt, kijk dus in de docs. Ik gebruik in het vervolg Perl als voorbeeld.
Voorbeeld
Laten we eenvoudig beginnen en eerst een kijken hoe zo'n regex er dan in het wild uitziet.
Stel, je wilt uit een bestand (waarin op elke regel een woord staat) de woorden halen waar een "a" in zit. Een stukje van de code zou zo kunnen zijn in Perl:
1
2
3
| while (<FILE>) { # lees woord in print if /a/; # print als er een "a" in zit } |
Wat er hier gebeurt is dat iedere keer een regel uit het bestand gelezen wordt (en de inhoud van die regel automagisch in de variabele $_ gekopieerd wordt). Vervolgens wordt gekeken of de regel (dwz de waarde van de magische $_ variabele) voldoet aan de regex /a/, en als dat zo is wordt de regel (dwz de waarde van de magische $_ variabele, die zo magisch is dat je hem niet eens ziet) geprint.
Zoals je ziet matcht de "a" in de regex /a/ zichzelf. Dit geldt voor de meeste tekens.
Hoe zou je kunnen kijken welke woorden "aa" (dus een "a" gevolgd door nog een "a") bevatten? Denk eerst even na...
Inderdaad, met de regex /aa/. Het achter elkaar zetten van tekens in een regex heeft tot gevolg dat de tekens in volgorde gematcht moeten worden.
Nou zei ik boven dat de meeste tekens zichzelf matchen, maar wat doen die andere tekens dan? Die andere tekens (metacharacters) zorgen ervoor dat er iets speciaals gebeurt, ze geven bijvoorbeeld een keuze of een herhaling aan.
Als een teken speciaal is kun je het zo speciaal maken dat het weer normaal wordt (dus: zichzelf matcht) door er een backslash voor te zetten. Op dezelfde manier kun je een normaal teken speciaal maken (zie hieronder).
Keuzes
Wat nu als je wilt kijken of een regel "aa" of "ee" bevat? Voor keuzes kun je het pipe symbool op de volgende manier gebruiken:
1
2
| /aa|ee/ # match "aa" OF "ee" in een string /aa\|ee/ # match "aa|ee" (letterlijk!) in een string |
Herhaling
Om aan te geven dat een bepaald teken een aantal keer herhaald moet/mag worden kun je de volgende constructies gebruiken:
1
2
3
4
5
6
7
8
9
| /a*/ # match 0 of meer keer "a" /a+/ # match 1 of meer keer "a" /a{1,2} # match 1 of twee keer "a" /a{1,} # match 1 of meer keer "a" /a{2}/ # match precies 2 keer "a" /a*/ # matcht 0 of meer keer "a" (bijv: '', 'a', 'aaaaaaa') /a+/ # matcht 1 of meer keer "a" (bijv: 'a', 'aaaaa') /a{3,5} # matcht drie tot vijf keer "a" (bijv: 'aaaa') /a{2,} # matcht twee of meer keer "a" (bijv: 'aaaaa') |
Uit zichzelf zal de regex engine proberen zo vaak mogelijk te matchen als het de keuze krijgt tussen minder of meer matchen op dezelfde plek. Dus (match in vierkante haken):
1
| /a+/ # matcht b[aaa]bbaaaaaa |
Je ziet dat /a+/ zo vaak mogelijk een "a" match. Of is dat niet zo? Verder naar recht staan meer"a"s dan hij er nu pakt!
Het belangrijke punt om te onthouden is dat een regex van links naar rechts werkt en houdt van "instant gratification" (als hij nu kan zorgen voor een complete match, waarom dan nog verder gaan? maar ook: als hij nu veel kan pakken in plaats van weinig, waarom dan met weinig tevreden zijn?). Lees het stukje hierboven nog eens en let op het vetgedrukte "op dezelfde plek".
Grouping
Om stukken van een regex te groeperen gebruik je haakjes, bijvoorbeeld:
1
2
3
4
5
| /aa|bb/ # matcht "aa" of "bb" /a(a|b)b/ # matcht "aab" of "abb" /ab{2}/ # matcht "abb" /(ab){2}/ # matcht "abab" /ab?a/ # matcht 'aa' en 'aba' |
Assertions
Soms wil je niet een bepaald teken matchen, maar wil je alleen kijken of iets waar is of niet.
Om aan te geven waar je wilt dat de match moet zijn kan je anchors gebruiken:
1
2
| /^a/ # begint met a /a$/ # eindigt met a |
De ^ zorgt er dus voor dat de match alleen slaagt als ^ kan matchen aan het begin van de string. ^ neemt geen ruimte in! (Idem voor $).
Ook kan je kijken of je op de grens van een "woord" bent (een "woord" bestaat in Perl uit letters, cijfer en de underscore, tenzij je met Unicode bezig bent) of juist niet:
1
2
3
4
| /\ber\b/ # "er" als los woord /\Ber\b/ # "er" aan het einde van een woord /\ber\b/ # "er" als los woord (bijv: "Het gaat er goed") /\Ber\b/ # "er" aan het einde van een woord (bijv: "Achter de molen") |
Character classes
Als kortere notatie voor keuzes tussen karakters kun je character classes gebruiken. Bijvoorbeeld om een teken te matchen dat een "a", een "b" of een "c" kan zijn kan beide onderstaande constructies gebruiken:
1
2
3
| /a|b|c/ # alternatie: de normale "keuze" /[abc]/ # character class van letters "a", "b" en "c" /[a-c]/ # idem van de range van "a" tot en met "c" |
Een character class is een opsomming van tekens, en binnen de character class (dus tussen de begin [ en de eind ] verliezen metacharacters als |, (, ), en . (waar ik het zo over heb) hun speciale betekenis.
1
| /[|(){}]/ # match het pipe symbool of een van de haken |
Om binnen een character class een "-" te gebruiken kan je hem aan het begin (direct na de openingshaak), aan het einde van de class zetten of escapen:
1
2
3
| /[ac-z-]/ # match een kleine letter behalve b, of de min /[-ac-z]/ # idem /[a\-c-z]/ # idem |
Kijk eens of je in de docs van jouw taal of tool kunt vinden hoe je een letterlijke "[" of "]" in een character class op kunt nemen.
Dit is als opstapje naar bijvoorbeeld PerlReTut en PerlRe voor details of PHP's PCRE en http://zez.org/article/articleprint/11/ of deze.
[ Voor 153% gewijzigd door NMe op 29-06-2005 13:07 ]
Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz