Regex Deel 3 - De puntjes op de i
Zoals je in het vorig deel hebt kunnen lezen is de regular expression tester te gebruiken om je regex kennis te praktiseren en expressies uit te testen.
Speciale karakters
Begin van een woord matchen
Het speciale karakter “\b” match een begin van een woord. Hebben we te doorzoeken tekst “Jan loopt met an in het bos met zijn os.” dan vinden we met de volgende reguliere expressie alleen een match op het woord “os”: \bos
Een match op de naam “an” maar niet op “Jan” (in dit voorbeeld zonder hoofdletters) verkrijgen we door: \ban
Om op elk woord een match te krijgen kan de volgende expressie worden gebruikt: \b\w+
Match op alle karakters
De “.” staat voor een match van alle karakters behalve row breaks en new line karakters: .+
Bovenstaande matcht de volledige tekst. Overigens heeft de punt (“.”) binnen een karakterklasse (“[.]”) geen speciale betekenis.
Carriage return, Line Feed en Tab
Carriage return en Line Feed worden binnen regex hetzelfde aangeduidt als binnen .NET strings.
- De carriage return:
\r
- De line feed:
\n
- De tab:
\t
Bij het matchen van meerder regels binnen een tekst zijn de karakters van groot belang.
Alternatieven (Alternation)
Binnen karakterklassen wordt er gematcht op karakters binnen de klasse. Dit werkt altijd als een OF. Buiten de klassen wordt standaard als EN gematcht. Een OF match maken op “maandag” of “dinsdag” of “woensdag” gaat via de volgende reguliere expressie:
1(maandag|dinsdag|woensdag)
De haken groeperen de opdracht. De pipes (“|”) zorgen voor de OF instructies.
Case (in)sensitive
1(?i)
Bovenstaande reguliere expressie maakt alles wat volgt case insensitive. De regulier expressie “(?i)jan” op de te doorzoeken tekst “Jan loopt in het bos” resulteert in een match op “Jan”.
1(?-i)
Bovenstaande expressie heft case insensivity weer op. Gretig of lui repeteren
Stel we hebben de tekst “Jan loopt in het bos. Hij speelt op zijn contrabas.” Als we elke zin willen matchen kunnen we de volgende reguliere expressie maken:
1[a-zA-Z\s]+
Dit zal resulteren in 2 resultaten (gebruik tab 2 in de test tool). Dit is een voorbeeld van greedy repetition. Het matchende resultaat wordt zo lang als mogelijk.
Voegen we echter een “?” aan het speciale repetitiekarakter (“+”) dan schakelen we over naar lazy repetition:
1[a-zA-Z\s]+?
Dit resulteert in 49 resultaten, voor elke letter en spatie 1 resultaat. Zodra er een match is is het afgelopen.
Vooruit en achteruit kijken
Het is mogelijk een match te maken op onderdelen in de tekst zonder ze mee te nemen in het resultaat. We hebben de tekst “Jan loopt in het bos.”. We willen een match op alles wat komt na de tekst “Jan”.
1(?<=Jan).*
.* staat voor alle karakters. ?<=Jan
is een lookbehind naar de tekst “Jan” zonder deze mee te nemen in het resultaat.
Willen we een match op alles wat komt voor de tekst “bos” dan gebruiken we de volgende regex:
1.*(?=bos)
Een combinatie van beide is:
1(?<=Jan).*(?=bos)
Alles tussen “Jan” en “bos” wordt gematcht.
Groeperen, verschillende resultaten in een keer matchen
In de tekst: “12 december 2010 19:34:44.999” zouden we bijvoorbeeld de dag en maand + het uur en de minuten kunnen halen. Groepen met namen zijn hier de oplossing. Zo kunnen in een keer op verschillende onderdelen van de tekst matches accuraat worden opgehaald. Dit is vooral handig bij het gebruik in .NET code. Een voorbeeld volgt iets verderop in dit artikel.
1(?[1-9][0-9]{0,1}\\s[a-zA-Z]+)\\s[1-9][0-9]{3}\\s(?[1-9][0-9]{0,1}:[0-9]{2}):[0-9]{2}[.][0-9]{3}
De syntax ?
binnen de ronde haken zorgt voor naamgeving van een groep. In .NET bevat elke match een groepcollectie waarin de namen en resultaten zijn terug te vinden.
Gebruik van reguliere expressies in .NET
Zoals in de eerste deel van de regex artikelen aangegeven bevindt de regex functionaliteit zich binnen .NET in de System.Text.RegularExpressions namespace. De methode die vooral worden gebruikt voor het valideren en zoeken in de class Regex zijn:
- IsMatch(string Text, string RegexPattern) resultaat Boolean
- Matches(string Text, string RegexPattern) resultaat MatchCollection object
Belangrijk is om NIET te vergeten de patterns te escapen:
1^\sbos.$
Wordt in code:
1Regex.IsMatch("Jan loopt in het bos.", "\\\\sbos.$");
Bij het gebruik van de Matches functie wordt een MatchCollection
object geretourneerd. Dit is een collectie met alle gevonden matches (Match objecten). De gevonden frase kan worden terug in de value property van het Match object.
1MatchCollection AllMatches = Regex.Matches("Jan loopt in het bos.", "n");
2foreach (Match m in AllMatches)
3{
4 string frase = m.Value;
5}
Zoals eerder genoemd kan het gebruik van groepsnamen heel handig zijn. In het derde tabblad van de regular expression tester kun je testen met groepen. Voorbeeld:
1string text = "12 december 2010 19:34:44.999";
2string regexPattern = "(?[1-9][0-9]{0,1}\\\\s[a-zA-Z]+)\\\\s[1-9][0-9]{3}\\\\s(?[1-9][0-9]{0,1}:[0-9]{2}):[0-9]{2}[.][0-9]{3}";
3MatchCollection Matches = Regex.Match(text, regexPattern); //er is 1 match
4int GroupCount = Matches[0].Groups.Count; //3 groepen, nl: 1. de gehele match, 2. Groep: DATUM en 3. Groep: TIJD)
5string Datum = Matches[0].Groups["DATUM"].Value; //of Matches[0].Groups[1].Value
6string Tijd = Matches[0].Groups["TIJD"].Value;
Conclusie
Er is nog veel meer te vertellen over regular expressions. Ik hoop dat dit helpt om een eind op weg te komen met regular expressions. Echt de puntjes op de spreekwoordelijke “i” ?. Het boek: Mastering Regular Expressions van O’Reilly vond ik heel leerzaam, het behandeld ook de toepassing van reguliere expressies in .NET.
Vragen of opmerkingen? Aarzel niet, elke reactie is van harte welkom!