JavaScript - L'objet "RegExp"
C'est un objet qui nous offre plus de contrôles sur les chaînes de caractères.
Regex,
RegExp, pour Regular Expression, ce qui se traduit par "
expression rationnelle" (une autre traduction, moins parlante, est "expression régulière").
 |
Il peut très vite devenir compliqué. |
Dans ce chapitre, les regex seront écrites
de cette façon
.
Sommaire partie 14
Sommaire
Créer une regex
On verra tout ce qu'il faut savoir pour créer une regex.
Quelques exemples
On va créer des regex pour vérifier :
- une adresse e-mail
- un numéro de téléphone français.
Utiliser une regex
Ici,
reg désigne une regex et
str une chaîne de caractères.
reg.test(str)
: renvoie true si str
vérifie la regex, false sinon.
reg.exec(str)
: applique la regex à la chaîne, renvoie le résultat.
str.match(reg)
: applique la regex à la chaîne, renvoie le(s) résultat(s).
str.replace(reg, str2)
: remplace le(s) sous-chaîne(s) vérifiant la regex par str2
et renvoie le résultat.
str.search(reg)
: renvoie la position de la première sous-chaîne vérifiant la regex.
str.split(reg)
: pour "découper" une chaîne.
Créer une regex
Créer une regex
Avant de se lancer…
Pour tester une regex, nous allons utiliser la méthode nommée
test
de cet objet, qui s'utilise ainsi :
Code : JavaScript
1 var verifie = maRegex.test(str);
Si la chaîne de caractères
str
vérifie la regex
maRegex
, alors
verifie
vaut
true, sinon il vaut
false.
Une regex… pourquoi ?!
 |
Comment ça fonctionne ? Ça veut dire quoi "vérifier une regex" ? |
Phrase pouvant décrire les regex :
Citation : les regex
L'utilisation des regex consiste à chercher un motif dans la chaîne de caractères.
Avec
test
, on récupère simplement
true si le motif est trouvé dans la chaîne de caractères,
false sinon.
Mais on peut également récupérer les morceaux de chaînes qui satisfont le motif, ou encore les remplacer par autre chose.
 |
Pour l'instant, il faut savoir qu'une chaîne de caractères est un motif.
On verra des motifs plus évolués un peu plus loin dans ce chapitre, c'est justement son but. |
Première regex
Créons une première regex. Pour cela deux façons, qui reviennent au même.
La première, grâce à laquelle on voit bien que
RegExp
est une classe d'objet :
Code : JavaScript
1 var maRegex = new RegExp("motif", "flags");
La seconde, dont la syntaxe est moins lourde :
Code : JavaScript
1 var maRegex = /motif/flags;
 |
Il y a des guillemets dans le premier cas, mais pas dans le second ! |
 |
Le paramètre flags est facultatif (dans les deux cas). |
On essaie !
Code : JavaScript
1 var reg = new RegExp("toto");
2 var resultat = reg.test("toto");
3 alert(resultat);
Ce qui affiche
true, normal.
Deuxième essai, avec la même regex :
Code : JavaScript
1 var reg = /toto/;
2 var resultat = reg.test("Bonjour toto !");
3 alert(resultat);
Ce qui affiche également
true, car la chaîne de caractères qui forme le motif (c'est "
toto
") a été trouvée dans notre phrase (qui est "
Bonjour toto !
").
Une regex un peu plus évoluée
Les flags
Comme on a vu, une regex est constituée de deux choses :
- un motif, qui sera recherché dans les chaînes de caractères auxquelles on appliquera notre regex,
- des flags, qui sont des options supplémentaires.
Revenons sur ces derniers. Ils sont au nombre de trois :
- i, pour rechercher le motif sans tenir compte de la casse (des majuscules / minuscules),
- g, signifiant "global", dont on précisera l'utilisation en temps voulu (quand on parlera des fonctions utilisant les regex),
- gi = g + i (pour combiner les deux options).
Des symboles bizarres
Revenons sur les motifs.
Des symboles particuliers vont nous aider à rendre nos motifs beaucoup plus évolués.
Le premier symbole est | (la barre verticale) et signifie OU.
Par exemple, pour vérifier si la chaîne contient sdz ou SdZ (avec ces majuscules) : /sdz|SdZ/
Cette regex vérifie "Vive le SdZ !" et "Je suis sur le sdz", mais pas "Le SDZ ?".
On peut utiliser des parenthèses pour délimiter : /(st|h|p)op/
est vérifié par "stop !", "Le shopping", "pop-up", mais pas par "flop".
Le symbole suivant est . (le point), qui signifie "n'importe quel caractère".
Ainsi, /o.o/i
(noter le flag i
pour ignorer la casse) sera satisfaite par : "o,O", "Toto" et "automobile".
Début et fin de chaîne
Deux symboles permettent de délimiter le début et la fin de la chaîne de caractères : ce sont respectivement ^ et $.
Par exemple, /^Bonjour/
sera satisfaite uniquement avec une phrase commençant par "Bonjour", comme "Bonjour monsieur", mais pas par "Hey ! Bonjour toi !".
Autre exemple, /!$/
va vérifier si une phrase se termine par un point d'exclamation, comme "Hey ! Bonjour toi !", mais pas comme "Toto ?!?"
Encore plus de symboles
Les classes
Il est également possible de définir, au lieu d'un seul caractère, une
liste de caract&eagrave;res (appelée
classe de caractères), en les indiquant entre crochets
[].
Avec
/t[iaot]c/
, on peut ainsi filtrer "
Et toc !", "
Tic, tac…", "
25? ttc", mais pas "
tuc".
A l'intérieur d'une classe, il est possible d'utiliser le tiret pour définir une
plage de valeur :
[i-n] équivaut à
[ijklmn], et
[0-9A-F] à
[0123456789ABCDEF].
On peut ainsi chercher si une phrase se termine par une lettre ou un chiffre avec
/[a-zA-Z0-9]$/
, vérifiée par "
Bonjour", "
BOB" et "
Agent 007", mais pas par "
Non." ni "
(-_-)".
Inversement, on peut définir une classe en indiquant les caractères qui ne doivent pas y figurer, grâce à
^ placé au début de cette classe.
Exemple : pour détecter les phrases qui ne se terminent pas par une lettre, on peut utiliser
/[^a-zA-Z]$/
, que vérifie "
Agent 007" mais pas "
Toto".
 |
Attention à ne pas confondre les deux utilisations de ^ !
- Au début d'une classe (entre crochets), il indique quels caractères ne font pas partie de cette classe.
- En dehors des crochets, il indique le début de la chaîne de caractères.
|
Classes existantes
Certaines classes très utilisées sont déjà définies : en voici les principales.
Nom | Equivalent | Description |
\d | [0-9] | Un chiffre |
\D | [^0-9] | Caractère qui n'est pas un chiffre |
\w | [a-zA-Z0-9_] | Caractère alpha-numérique, ou underscore |
\W | [^a-zA-Z0-9_] | Caractère non alpha-numérique (autre que underscore) |
\s | | Caractère "blanc" (espace, saut de ligne, tabulation, etc.) |
\S | | Caractère "non-blanc" |
\b | | Début / fin de mot (début de chaîne, espace, etc.) |
\B | | Ni début ni fin de mot |
 |
Dans une chaîne de caractères, l'antislash est déjà utilisé pour les caractères spéciaux. Ainsi, si on utilise la notation new RegExp("motif") ,
il faut faire précéder chaque antislash d'un… antislash.
Par exemple, les deux regex suivantes sont identiques :
Code : JavaScript
1 var regex1 = /\d/;
2 var regex1 = new RegExp("\\d");
|
Les quantificateurs
Il est possible de préciser le nombre de fois que doit se répéter un "bout de motif".
Quantifier "manuellement"
On peut préciser, entre accolades {}, le nombre de fois qu'un caractère / groupe de caractères doit se répéter.
Dans cet exemple, on veut filtrer "foot" avec deux "o" : /fo{2}t/
Il est possible d'indiquer une plage de valeur : on demande au groupe de caractères de se répéter au minimum i
fois, et au maximum j
fois, avec {i,j}
.
Par exemple, /bo{2,5}m/
cherchera "boom" avec 2, 3, 4 ou 5 "o".
Noter que la valeur supérieure est facultative.
Ainsi, /bo{2,}m/
sera validé par "boom" et par "boooooom" : avec 2 "o" ou plus.
D'autres symboles pour quantifier
Il existe les symboles
? * et
+ en guise de "raccourcis".
Nom | Equivalent | En français |
? | {0,1} | 0 ou 1 fois |
* | {0,} | N'importe quel nombre de fois |
+ | {1,} | Au moins une fois |
Symbole utilisé = symbole à échapper !
 |
Et si on veut utiliser le point dans nos regex, comment faire ?
Si on l'écrit comme ça, il veut dire "n'importe quelle caractère" ! |
Pour utiliser en dehors d'une classe l'un des symboles, mais en tant que caractère, il faut l'échapper en le faisant précéder d'un antislash.
Exemple : pour savoir si une chaîne se termine par ".com", on utilisera
/\.com/
Voici une liste de symboles à échapper :
| . ^ $ ( ) [ ] - { } ? * + \ /
A l'intérieur d'une classe, inutile d'échapper les caractères autres que
^ [ ] - \
 |
De même que pour les classes prédéfinies, si on utilise la notation new RegExp("motif") , il faut penser à échapper les antislashes, ainsi que les guillemets !
Voici à nouveau deux regex identiques :
Code : JavaScript
1 var regex1 = /\.com/;
2 var regex1 = new RegExp("/\\.com/");
|
Parenthèses : avec ou sans capture ?
Ce qu'on n'a pas encore dit…
 |
Pour l'instant, nous utilisons simplement les regex pour savoir si une chaîne vérifiait un certain motif. Mais les regex permettent bien plus que ça ! |
Il est également possible de savoir quelles sous-chaînes satisfont le motif, et on va même pouvoir utiliser les regex pour en extraire certains morceaux.
Ainsi, dans une phrase telle que "les horaires sont 16h45, 17h, 17h20 et 17h40", il est possible d'extraire toutes les heures à l'aide d'une simple regex. Puissant !
Les parenthèses capturantes
Pour extraire certains morceaux d'une chaîne de caractères, il faut les délimiter dans notre motif. Pour ce faire, on utilise les parenthèses, qui sont dites capturantes.
Un exemple : on veut extraire l'âge du visiteur, qui se trouve dans une chaîne du type "J'ai 99 ans".
Construisons notre motif lentement&hellip on va d'abord se baser sur "j'ai" et "ans", sans tenir compte de la casse : /j\'ai - ans/i
Ces mots sont séparés de l'âge par un blanc (éventuellement, l'utilisateur peut écrire "99ans" sans espace) : /j\'ai\s - \s?ans/i
Maintenant, on ajoute les chiffres (1, 2 ou 3 chiffres), en les capturant grâce aux parenthèses : /j\'ai\s(\d{1,3})\s?ans/i
On a donc notre regex /j\'ai\s(\d{1,3})\s?ans/i
Elle va nous permettre d'extraire l'âge (dans notre exemple, 99).
Pour l'instant, on ne sait pas comment récupérer le contenu ; en fait, ça va dépendre de la fonction utilisée, on en reparlera donc quand on présentera les fonctions.
Et si on veut des parenthèses qui ne capturent pas ?
Cependant, on peut avoir besoin de parenthèses, mais sans pour autant vouloir en capturer le contenu (par exemple, avec le symbole | vu plus haut).
Il est possible de les rendre non-capturantes, en les faisant commencer par ?:
.
Reprenons un exemple vu plus haut, en rendant les parenthèses non capturantes : /(?:st|h|p)op/
Les assertions
Terminons la théorie par un point un peu plus délicat.
Supposons qu'on veuille rechercher "
bon", mais uniquement dans le mot "
bonjour"…
On pourrait utiliser des parenthèses capturantes, de cette manière :
/(bon)jour/
.
Ainsi, "Bien le
bonjour, mon bon monsieur" vérifiera ce motif.
On récupère donc deux choses : le "bonjour", qui satisfait le motif, et le "bon", capturé par les parenthèses.
Mais il est possible d'utiliser ce qu'on appelle une assertion : le symbole
(?=
(à placer entre parenthèses) à la fin du motif, signifie "
si le motif est suivi de".
Dans l'exemple précédent, ceci nous donne
/bon(?=jour)/
. Comprenez-le ainsi : "
bon, s'il est suivi de
jour".
Ainsi, dans la phrase "Bien le
bonjour, mon bon monsieur", seul le "
bon", s'il est suivi de "
jour", est sélectionné.
Il existe de même le symbole
?!
, en fin de motif lui aussi, signifiant "
si le motif n'est pas suivi de".
Ainsi,
/bon(?!jour)/
(comprendre : "
bon, s'il n'est pas suivi de
jour") appliqué à la même phrase nous donnera ce résultat : "Bien le bonjour, mon
bon monsieur".
 |
Remarquer qu'on retrouve le symbole = pour "est suivi de", et ! pour "n'est pas suivi de".
Comme pour les signes == et !=, le = exprime l'égalité, et le ! la négation. |
Quelques exemples
 |
Quand est-ce qu'on utilise des expressions régulières ?
Parce que c'est bien joli de rechercher "toto" dans une phrase, mais en pratique&hellip |
En JavaScript, il est courant d'avoir à vérifier des formulaires avant de les valider. Et pour cela, les regex constituent un outil
excellent, car elles permettent de contrôler très précisément toutes les informations données par le visiteur.
 |
Comme on l'a dit, vérifier un formulaire en JavaScript est très agréable pour le visiteur, mais n'est pas très fiable (car ce dernier peut désactiver le JS pour contourner la vérification).
Ainsi, il est courant d'effectuer une seconde vérification, en PHP cette fois, pour une vérification réellement fiable.
Et en PHP, les regex existent, et sont pratiquement identiques, ce qui nous simplifie donc la tâche si on doit faire les deux vérifications. |
Revenons à notre sujet : nous voulions créer des regex pour vérifier si le pseudonyme donné par le visiteur n'est pas trop long, si l'âge qu'il a saisi est valide, s'il n'a pas donné une adresse e-mail qui n'en est pas une, si son numéro de téléphone est vraisemblable, etc.
Le pseudonyme
Tout d'abord, on demande un pseudonyme à l'utilisateur.
Seulement voilà : pour éviter les trucs complètement tordus qui risquent de nous embêter par la suite, on va demander à ce qu'il comporte entre 3 et 16 lettres, avec éventuellement des chiffres et des tirets (hauts et bas).
Voici donc une regex, toute simple, pour vérifier ceci :
/^[a-zA-Z0-9_-]{3,16}$/
Peut être adapté si on a d'autres besoins.
L'âge
Pour vérifier l'âge, il y a deux solutions…
La première consiste à convertir la valeur en nombre (entier), pour la comparer à un âge minimum et à un âge maximum.
Prenons par exemple 5 et 100 ans comme âges limites, et créons une fonction qui renvoie
true si l'âge passé en paramètre est valide :
Code : JavaScript
1 function ageValide(valeur)
2 {
3 var age = parseInt(valeur); // on convertit la valeur en nombre entier
4 var estValide = !isNaN(age) && (age >= 5) && (age <= 100);
5 return estValide;
6 }
 |
Les deux dernières lignes de la fonction sont équivalentes à ceci :
Code : JavaScript
1 if(!isNaN(age) && (age >= 5) && (age <= 100))
2 return true;
3 else
4 return false;
|
La seconde solution : en utilisant une regex !
On veut que l'âge soit composé d'un ou deux chiffres (on pourrait en prendre 3, ça ne change pas grand-chose au final).
Pour rappel, la classe
[0-9]
peut s'écrire
\d
.
Ce qui nous donne :
/^\d{1,2}$/
 |
Quelle solution privilégier ?
- La première solution a l'avantage de bien s'adapter, pour deux raisons. Tout d'abord, on choisit précisément les limites d'âge. Ensuite, la fonction de conversion est capable de "comprendre" correctement une expression telle "99 ans", qui sera ici considérée comme valide.
- Concernant la seconde solution, certain diront que "c'est comme tuer une mouche avec un bazooka".
En bon français : est-ce vraiment utile d'utiliser des moyens si "élaborés" pour tester un nombre ?
En effet, les regex sont utilisés pour analyser des chaînes de caractères. Les utiliser pour savoir si on se trouve en présence d'un nombre peut donc sembler un peu "démesuré".
|
Le numéro de téléphone
Passons maintenant au numéro de téléphone, dans le cas où c'est un numéro français (le principe reste bien sûr le même pour tous les pays).
Pour rappel, un numéro français est constitué de 5 groupes de 2 chiffres.
Le premier chiffre est toujours un 0 ; le second chiffre peut être soit 1, 2, 3, 4, 5 (selon la région), soit 6 (pour les portables), soit 8 (numéros spéciaux).
Pour séparer les groupes de chiffres, on va considérer que l'utilisateur utilisera soit un espace, soit un tiret, soit un point (soit il ne les sépare pas).
Au passage, nous allons choisir de capturer chacun des groupes de chiffres (ce qui permettra, si besoin est, de ne retenir que les chiffres, sans les séparateurs).
Décomposons donc notre regex…
/[ _.-]?/
pour un séparateur (rappel : le ?
est équivalent à {0,1}
, car le séparateur est facultatif).
/[ _.-]?(\d{2})/
: on ajoute deux chiffres après le séparateur, et on les capture grâce aux parenthèses.
/(?:[ _.-]?(\d{2})){4}/
: on répète 4 fois ce groupe "séparateur + 2 chiffre". Pour le délimiter, on utilise des parenthèses non capturantes.
Pour l'instant, on capture donc les numéros tels que "-12-34-56-78".
/(0[1-68])(?:[ _.-]?(\d{2})){4}/
: on ajoute (et on capture avec les parenthèses) le premier groupe de 2 chiffres, dont le premier est forcément un 0, et le second est 1, 2, 3, 4, 5, 6 ou 8.
/^(0[1-68])(?:[ _.-]?(\d{2})){4}$/
: on ancre la regex : le numéro commence au début de la chaîne et se termine à la fin.
Ce qui nous donne la magnifique regex suivante, prête à l'emploi :
Code : JavaScript
1 var regexTel = /^(0[1-68])(?:[ _.-]?(\d{2})){4}$/
;
Ou bien, pour utiliser l'autre façon de créer une regex :
Code : JavaScript
1 var regexTel = new RegExp("^(0[1-68])(?:[ _.-]?(\\d{2})){4}$"
);
Pour tester cette regex, voici un petit script pour demander un numéro et le tester.
Code : JavaScript
1 var regexTel = /^(0[1-68])(?:[ _.-]?(\d{2})){4}$/
;
2 var numeroTel = prompt("Quel est votre numéro de téléphone ?");
3 if(regexTel.test(numeroTel))
4 alert("Votre numéro semble correct");
5 else
6 alert("Ce n'est pas un numéro de téléphone (français) valide");
L'adresse e-mail
Une adresse e-mail, c'est :
- des caractères : lettres, chiffres, points et tirets (hauts ou bas),
- un symbole "arobase" @,
- des caractères : comme au début, mais en minuscules ; au moins deux,
- un point,
- des caractères : de 2 à 4 lettres minuscules.
Donc pour la regex :
/^[a-zA-Z0-9._-]+@[a-z0-9._-]{2,}\.[a-z]{2,4}$/
Utiliser une regex
reg.test(str)
Renvoie true si la chaine vérifie le motif, false sinon.
reg.exec(str)
Cherche la première expression vérifiant le motif, et renvoie un tableau contenant :
- dans la première case (d'indice 0), l'expression qui vérifie le motif,
- dans les cases suivantes (à partir de l'indice 1), les sous-chaînes capturées via les parenthèses.
str.match(reg)
Renvoie un tableau :
- s'il n'y a pas le flag "
g
" : le tableau contient la première expression qui satisfait le motif, et les captures (même résultat que reg.exec(str)
) ;
- s'il y a le flag "
g
" : le tableau contient toutes les expressions satisfaisant le motif (mais sans les captures).
str.search(reg)
Renvoie la position de la première expression qui vérifie le motif.
str.replace(reg, str2)
Remplace les expressions vérifiant le motif par la chaine
str2
(uniquement la première s'il n'y a pas le flag "
g
", toutes s'il y est) ; le résultat est renvoyé.
La valeur capturée par la n-ième parenthèse (
n
étant un numéro, plus grand ou égal à 1) est accessible via
$n
.
Exemple : affichera "Le grooos chat griiis est très graaas !"
Code : JavaScript
1 var regex = /gr([aio])s/g
;
2 var chaine = "Le gros chat gris est très gras !";
3 var resultat = chaine.replace(regex, "gr$1$1$1s"
);
4 alert(resultat);
str.split(reg)
Découpe la chaine aux endroits où le motif est vérifié. Les éléments capturés via les parenthèses sont rajoutés au découpage.
Exemple pour scinder une chaîne là où il y a des signes de ponctuation, en conservant ces signes (pour rappel,
\s
= caractère blanc) :
Code : JavaScript
1 var regex = /\s?([.,:;])\s?/
;
2 var chaine = "Ceci est un test : il est destiné à tester des regex ; à rien d'autre, rien.";
3 var resultat = chaine.split(regex);
Voici le résultat obtenu :
Citation : Contenu du tableau
resultat[0] : "Ceci est un test"
resultat[1] : ":"
resultat[2] : "il est destiné à tester des regex"
resultat[3] : ";"
resultat[4] : "à rien d'autre"
resultat[5] : ","
resultat[6] : "rien"
resultat[7] : "."
resultat[8] : ""(car il n'y a rien après le point)
 |
Les regex sont des outils vraiment puissants. Mais il ne faut pas non plus en abuser, car ça peut demander beaucoup de travail à l'ordinateur.
Les utiliser uniquement lorsqu'elles s'avèrent vraiment utiles. |