Het gebruik van character classes

Hoewel je al een hoop kan doen met letterlijke regexps zijn zowel alle voorbeelden hierboven ook de doen met substr(). Dit is dan ook maar het topje van de ijsberg van wat mogelijk is met reguliere expressies. In deze en de hieropvolgende secties zullen we regexp concepten (en bijbehorende metacharacter notaties) behandelen die een regexp in staat stellen om niet alleen een rij teken of letter te matchen maar een hele class (een set tekens).

Een zo'n voorbeeld is een character class. Een character class zorgt er voor dat je een set van mogelijke tekens, in plaats van een enkel teken kan matchen op een bepaald pun in een regexp. Character classes worden gescheiden door brackets [...], met een set van tekens die mogelijk matchen daarin.

Voorbeeld:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?
preg_match("/cat/", $string); // matched 'cat'
preg_match("/[bcr]at/", $string); // matched 'bat, 'cat', or 'rat'
preg_match("/item[0123456789]//", $string);  // matched 'item0' of ... of 'item9'
preg_match("/[cab]/","abc");    // matched 'a'
?>


In het laatste voorbeeld matched 'a', ook al staat 'c' als eerste in de regex. 'a' matched omdat het het eerste teken is in de string.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?
preg_match("/[yY][eE][sS]/",$string);
// Matched 'yes' in een hoofdletterongevoelige
// manier: yes, Yes, yEs, YES, yeS, etc.

?>


Deze regexp geeft een veel voorkomend probleem weer: en case insensitive match. Dit is mogelijk door een /i toe te voegen aan het eind van de string. i, staat voor case insensitive, en is een voorbeeld van een modifier. Op deze manier kan de vorige regex worden herschreven als:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?
preg_match("/[y][e][s]/i", $string);
?>


We zagen eerder dat er gewone tekens zijn en speciale tekens die ene backslash nodig hebben om letterlijk te gebruiken. Het zelfde geld in een character classe, maar binnen een character class moeten iets andere tekens geescaped worden. Namelijk:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
-]\^$


Zie hier een voorbeeld hoe je dit moet oplossen:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
<?
preg_match("/[\]c]def/",$string); // Matched ']def' of 'cdef'

$x = 'bcr';
preg_match("/[$x]at/", $string);  // matched 'bat', 'cat' en 'rat'
preg_match("/[\$x]at/", $string); // matched '$at' of 'xat'
preg_match("/[\\$x]at/", $string);// matched '\at\', 'bat', 'cat' of 'rat'

?>


De laatste twee zijn een beetje tricky. in [\$x], zorgt de backslash ervoor dat $ letterlijk geintrepeteerd moet worden, zodat het twee onderdelen heeft: $ en x. in [\\$x] wordt $x als een variabele beschouwd en bestaat de set uit \ en de variabelen in $x.

Update: de verwarringsloze versie is uiteraard waarbij je netjes variabelen buiten quotes zet:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?
preg_match("/[".$x."]at/", $string);  // matched 'bat', 'cat' en 'rat'
?>


Het - teken zorgt ervoor dat je een hele reeks aan tekens kan aangeven in een character class. Zo dat een reeks opeenvolgende tekens kan worden herschreven als een reeks. Met reeksen voorkom je enorme regexps als [0123456789] en [abcdefghijklmnopqrstuvwxyz], dit wordt [0-9] en [a-z].

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?
preg_match("/item[0-9]/",$string);    // matched item0 tot item9.
preg_match("/[0-9bx-z]aa/", $string); // matched '0aa' tot '9aa' en 'baa' en 'xaa', 'yaa' of 'zaa'
preg_match("/0-9a-fA-F/", $string);   // matched hexadecimale getallen
preg_match("/0-9a-zA-Z_/", $string);  // matched een "woord" teken zoals in een variabelenaam/
?>


Wanneer - het eerst of laatste teken is in een character class wordt het als een gewoon teken beschouwd. [-ab] [ab-] en [a\-b] hebben het zelfde resultaat.

Het speciale teken ^ in de eerste positie in een character class betekend dat alle tekens behalve de tekens binnen de brackets [] zullen matchen.

Dus:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?
preg_match("/[^a]at/", $string) // matched niet met aat of at, maar wel met bat, cat, 0at, %at etc.
preg_match("/[^0-9]/", $string) // matched alles behalve cijfers
preg_match("/[a^]at/", $string)  // matched 'aat' of '^at' ^ wordt hier als een gewoon teken beschouwd.
?>


Afkortingen character classes
Zelfs [0-9] kan vervelend zijn om meerdere keren te schrijven dus om het aantal aanslagen te beperken zijn er een paar afkoringen voor veel gebruikte charachter classes:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
\d  is een nummer, en staat voor [0-9]
\s  is een whitespace (spatie, enter, carriage return, tab) teken en staat voor [\ \t\r\n\f]
\w  is een woord teken en staat voor [0-9a-zA-Z_]
\D  is het omgekeerde van een nummer en dus [^0-9]
\S  is het omgekeerde van whitespace [^\ \t\r\n\f]
\W  is het omgekeerde van een woord teken [^0-9a-zA-Z_]
het '.' teken zal elk teken matchen behalve '\n'.


De \d\s\w\D\S\W afkortingen kunnen zowel binnen als buiten character classes gebruik worden.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
<?
preg_match("/\d\d:\d\d:\d\d/", $string); // matched een uu:mm:ss tijd formaat
preg_match("/[\d\s]/", $string);         // matched een nummer of een whitespace teken
preg_match("/\w\W\w/", $string);         // matched een woord teken gevolgd door een
                                         // niet-woord teken gevolgd door een woordt teken.

preg_match("/..rt/", $string);           // matched twee tekens gevolgd door 'rt'
preg_match("/end\./, $string);           // matched 'end.'
?>


Omdat . een metacharacter is moet het worden escaped om als een gewone punt gematched te worden.

Een anchor die handig is in simpele regexps is de word anchor \b. Deze matched de grens tussen een woord teken een niet-woord teken. Dus: waar een woordt begint of eindigd. Voor de techneuten onder ons: tussen \w\W of \W\w.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
<?
$x
= "Housecat catenates house and cat";
preg_match("/cat/", $string);    // matches cat in 'housecat'
preg_match("/\bcat/", $string);  // matches cat in 'catenates'
preg_match("/cat\b/", $string);  // matches cat in 'housecat'
preg_match("/\bcat\b/", $string);// matches 'cat' at end of string
?>


In het laaste voorbeeld wordt het einde van de string gezien als een woordgrens.

Mocht je je afvragen waarom '.' elk teken behalve "\n" (\n is een enter teken) matched? De reden is dat we vaak alleen de regel willen matchen en de enter willen negeren. Bijvoorbeeld, wanneer \n een regel betekend willen we er graag over denken als leeg. In dat geval:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
<?
preg_match("/^$/", "");   // matched
preg_match("/^$/", "\n"); // matched, "\n" word genegeerd
preg_match("/./", "");    // matched niet, het heeft een teken nodig
preg_match("/^.$/", "");  // matched niet, het heeft een teken nodig
preg_match("/^.$/", "\n");// matched niet, het heeft een teken nodig anders dan een enter
preg_match("/^.$", "a");  // matched
preg_match("/^.$", "a\n");// matched, de \n wordt genegeerd
?>


Dit gegrag is handig omdat we normaal gesproken de enters willen negeren en we de gehele string in een keer willen matchen. Soms wil je dat \n tekens wel meetellen, en dat ^ en $ niet het begin en eind van de string betekenen maar het begin en eind van een regel. In dit geval worden de //s en //m modifiers aangeboden om wel of niet op enters te letten in een string. Kortom: is het een set regels (bijvoorbeeld in een cvs bestand, waarin elke regel een aparte regel is? Of een doorlopende string waarin enters zitten, bijvoorbeeld in een comment op een website of een forum?

Deze twee modifiers beinvloeden hoe de regexp wordt geintrepeteerd.

1) hoe is de '.' character class gedefinieerd?
2) waar matchen de anchors ^ en $?

Hier zijn de vier mogelijke combinaties:

- geen modifiers: (//). Standaard gedrag. De '.' matched elk teken behandeld "\n". ^ matched alleen aan het begin van een string, en $ matched alleen aan het eind of voor een \n aan het eind.

- s modifier (//s). Behandel een string als één lange regel. \n matched elk teken, ook \n. ^ matched alleen aan het begin of aan het eind van een string.

- m modifier (//m). Behandel een string als een set van meedere regels. '.' matched elk teken, behalve \n. ^ en $ matchen het begin van een regel binnen de string.

- s en m modifier (//sm). Behandel de string als een set van meerdere regels. '.' maar detecteer meerdere regels. '.' zal elk teken matchen, ook \n, maar ^ en $ zullen aan het begin en eind van elke regel binnen de string matchen.

Zie hier de voorbeelden van //s en //m:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
<?
$x
= "There once was a girl\nWho programmed in Perl\n";
preg_match("/^Who/", $string);       // doesn't match, "Who" not at start of string
preg_match("/^Who/s", $string), $string);  # doesn't match, "Who" not at start of string
preg_match("/^Who/m", $string);      // matches, "Who" at start of second line
preg_match("/^Who/sm", $string);     // matches, "Who" at start of second line
preg_match("/girl.Who/", $string);   // doesn't match, "." doesn't match "\n"
preg_match("/girl.Who/s", $string);  // matches, "." matches "\n"
preg_match("/girl.Who/m", $string);  // doesn't match, "." doesn't match "\n"
preg_match("/girl.Who/sm", $string); // matches, "." matches "\n"
?>


Meestal is het het standaard gedrag wat je wil, maar //s en //m zijn soms erg handig. Wanner //m wordt gebruik kan je nog steeds het begin en eind van de de string matchen met de anchors: \A en \Z (deze matchen ook de enters aan ervoor of erna)

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
<?
preg_match("/^Who/m", $string);   // matches, "Who" at start of second line
preg_match("/\AWho/m", $string);  // doesn't match, "Who" is not at start of string
preg_match("/girl$/m", $string);  // matches, "girl" at end of first line
preg_match("/girl\Z/m", $string); // doesn't match, "girl" is not at end of string
preg_match("/Perl\Z/m", $string); // matches, "Perl" is at newline before end
preg_match("/Perl\z/m", $string); // doesn't match, "Perl" is not at end of string
?>


Dit was het voor de charachter classes. In het volgende deel wordt uigelegd hoe je verschillende sets kan matchen.

« Lees de omschrijving en reacties

Inhoudsopgave

  1. Voorwoord
  2. My First Regex
  3. Het gebruik van character classes
  4. Het een of het ander matchen
  5. Groeperen en hierarchisch matchen
  6. Het extraheren van matches
  7. Repeterende matches
  8. Disclaimer, bronvermelding, handige links, TODO

PHP tutorial opties

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.