Les regex ou expressions régulières en JavaScript

Auteur

Clément Nerma

...
Commentez

- Titre : Les regex ou expressions régulières en JavaScript

- Auteur : Clément Nerma

- Parution : 12 mai 2015

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2015 Clément Nerma. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Pré-requis

- II. Découverte

- II.1. Introduction

- II.2. POSIX et PCRE

- II.3. Définir une expression régulière

- II.4. Contexte d'utilisation

- II.4-A. La fonction String.match()

- II.4-B. L'objet RegExp

- II.4-C. Les différents caractères spéciaux des regex

- II.4-D. Un p'tit T.P. ?

- III. Utilisation des regex pour remplacement

- III.1. Remplacement basique et flags

- III.2. Remplacement avec données de regex

- III.3. Remplacement par fonction

- IV. T.P. final

- V. Remerciements

I - Pré-requis

Pour comprendre cet article, vous devez savoir programmer en JavaScript et connaître les bases du langage, à  savoir délarer et modifier une variable, récupérer une partie d'une chaîne de caractères, etc.

II - Découverte

II.1 - Introduction

Si vous avez déjà  ouvert un fichier JavaScript d'un autre développeur, vous avez peut-être déjà  vu un code similaire au suivant :

if(! pseudo.match(/^([a-zA-Z ]+)$/))
alert('Pseudo invalide');

Ce petit bout de code /^([a-zA-Z ]+)$/ se nomme regex, ou expression régulière. Cela permet, entre autre, de vérifier le contenu d'une chaîne de caractères.

II.2 - POSIX et PCRE

Vous avez peut-être déjà  vu apparaître ces deux termes au cours d'une discussion entre développeurs ou tout simplement dans un article. Il s'agit des deux grands types de regex que l'on retrouve dans pas mal de langages de programmations.

Si vous avez déjà  fait du Perl, vous avez peut-être déjà  utilisé les regex PCRE. Si vous avez déjà  fait du PHP, vous avez soit fait des regex POSIX, soit PCRE, puisque les deux sont supportés par PHP.

En JavaScript, seules les regex PCRE sont supportées (comme Perl) !

II.3 - Définir une expression régulière

Pour définir une expression régulière, il faut la placer entre deux slashs (/), et peut être placée dans une variable, comme ceci :

var regex = /leContenuDeMaRegex/;

Les chaînes de caractères sont définies entre guillemets ('), les regex entre des slashs (/) !

II.4 - Contexte d'utilisation

Une regex peut être utilisé via deux méthodes :

- soit en utilisant la fonction match() que nous allons voir tout de suite ;

- soit en utilisant l'objet RegExp que nous aborderons un peu après.

II.4-A - La fonction String.match()

Lorsque vous utilisez une chaîne de caractères, vous pouvez lui appliquer la fonction match(), de la manière suivante :

var message = 'Bonjour les gens !';
var regex = /les/;
if(message.match(regex))
alert('Tiens, il y a plusieurs personnes ?');
else
alert('Tout seul...');

Si vous exécutez ce code, vous obtiendrez une alerte "Tiens, il y a plusieurs personnes ?". En effet, la regex /les/ vérifie que "les" est présent au moins une fois dans la chaîne de caractère message (qu'on peut appeler sujet de l'expression régulière).

Notez que j'aurais très bien pu écrire le code de cette manière :

var message = 'Bonjour les gens !';
if(message.match(/les/))
alert('Tiens, il y a plusieurs personnes ?');
else
alert('Tout seul...');

C'est vraiment similaire aux chaînes de caractères.

II.4-B - L'objet RegExp

Vous pouvez aussi utiliser l'objet RegExp pour créer des expressions régulières. Dans ce cas, la fonction match() fonctionnera très bien aussi.

var message = 'Bonjour les gens !';
var regex = new RegExp('les');
if(message.match(regex))
alert('Tiens, il y a plusieurs personnes ?');
else
alert('Tout seul...');

Vous voyez que j'ai juste créé un objet RegExp. Notez qu'il n'y a pas besoin d'écrire les / lorsque l'on utilise l'objet RegExp. Cela permet (notamment) de créer une regex à partir d'une chaîne de caractères.

Par exemple, le code suivant ne peut fonctionner qu'à partir d'une RegExp :

var prompt_regex = prompt('Votre regex ?', '');
var prompt_message = prompt('Votre message ?', '');

var regex = new RegExp(prompt_regex);

if(prompt_message.mach(prompt_message))
alert('La regex a trouvé quelque chose !');
else
alert('La regex n'a rien trouvé ;-(');

Car en effet, vous ne pouvez pas écrire :

var regex = // + prompt_regex;

Cela génèrera une erreur.

II.4-C - Les différents caractères spéciaux des regex

Les regex utilisent des caractères spéciaux pour pouvoir chercher plus facilement une information dans une chaîne de caractères. Et leur but est de vous faire gagner du temps et du code.

La preuve, si vous voulez que l'utilisateur saisisse un code, et que vous voulez que celui-ci soit bien constitué uniquement de 4 chiffres, vous devrez effectuer le code suivant sans regex :

var code = prompt('Le code svp ?', '');
var error = false;

for(var i = 0; i < 4; i++)
if(!code[i] == '0' || !code[i] == '1' || !code[i] == '2' || !code[i] == '3' || !code[i] == '4' || !code[i] == '5' || !code[i] == '6' || !code[i] == '7' || !code[i] == '8' || !code[i] == '9') {
error = true;
break;
}

if(!error)
alert('Le code semble correct...');
else
alert('Mauvais format de code ! (4 chiffres)');

Et voici ce que Ã§a donne avec une regex :

var code = prompt('Le code svp ?', '');
var error = false;

if(!code.match(/^([0-9]){4}$/))
alert('Le code semble correct...');
else
alert('Mauvais format de code ! (4 chiffres)');

Avouez qu'on gagne quand même à utiliser des regex, hein ? :-) Et c'est encore pire pour vérifier une adresse e-mail, par exemple !

Analysons ensemble cette regex.

- Le ^ indique que la chaîne de caractères doit commencer par ce qui est marqué après.

- Le $ indique que la chaîne de caractères doit se terminer par ce qui est marqué avant.

Vous l'aurez compris, une regex entourée d'un ^ et d'un $ signifie que toute la chaîne de caractères doit correspondre. Par exemple, "les gens" fonctionne avec /les/, avec /^les/, mais pas avec /^les$/ ou même /les$/.

- Les caractères situés entre les deux crochets [ et ] permettent de dire "n'importe quoi mais qui est marqué entre ces crochets". Par exemple, [0-9] veut dire "n'importe quel chiffre", [abc] veut dire "la lettre a, la lettre b ou la lettre c". On peut utiliser dans une regex :

- Les parenthèses permettent de spécifier son contenu comme une donnée de la regex. On verra Ã§a plus tard, ne vous inquiétez pas.

- Un chiffre entre crochets permet de dire "ce qu'il y a avant, répété x fois". Par exemple, ([0-9]){4} signifie "un chiffre, répété 4 fois", soit 1456, 1795, 7513, etc. On peut également dire "au moins 4 fois" si le code fait minimum 4 chiffres, en écrivant ([0-9]){4,}. Ou encore "2 à 4 fois" avec ([0-9]){2,4}.

Vous voyez, les expressions régulières sont vraiment très utiles lorsqu'il s'agit de vérifier le contenu d'une chaîne de caractères !

Comprendre les regex n'est pas chose facile, si vous n'avez pas tout compris, n'hésitez pas à relire cette partie du cours !

Les répétitions

Si vous voulez que la chaîne de caractères donnée contienne "il y a x personnes" (il y a 8 personnes, il y a 45 personnes, etc.), vous ne pouvez pas seulement utiliser un ([0-9]). Vous devez vérifier qu'il y a au moins un chiffre, sinon plusieurs. La solution que nous avons vue est de faire :

/^il y a ([0-9]){1,} personnes$/

Mais on peut le faire encore plus facilement en utilisant un opérateur avant la parenthèse fermante :

/^il y a ([0-9]+) personnes$/

Le + permet de dire "au moins une fois", il "remplace" le {1,} (en fait c'est plus complexe que Ã§a, mais nous le verrons plus tard). Si j'avais mis un ?, Ã§a aurait voulu dire "optionnel", c'est-à-dire soit 0 fois, soit 1, soit 2, etc. Il "remplace" le {0,}

II.4-D - Un p'tit T.P. ?

1) Sujet

Voyons voir si vous avez bien tout compris ! Vous allez devoir créer une regex qui permet de vérifier que le pseudo saisit par l'utilisateur contient bien des chiffres, des lettres ou des tirets bas, et fait de 6 à 20 caractères de long. A vos marques, prêts, codez !

Ne lisez pas la suite du cours à moins de ne pas du tout réussir à créer cette regex !

2) Correction

Tout d'abord, nous devons vérifier que le pseudo contient des lettres, des chiffres ou des tirets bas (_). On fait donc :

([0-9a-zA-Z_])

Maintenant, il faut qu'il mesure entre 6 et 20 caractères, donc :

(([0-9a-zA-Z_]){6,20}

Enfin, il doit être UNIQUEMENT composé de cela, donc commencer et termine par notre regex :

^([0-9a-zA-Z_]){6,20}$

Et le code final :

var pseudo = prompt('Votre pseudo ?', '');

if(pseudo.match(/^([0-9a-zA-Z_]){6,20}$/))
alert('Pseudo correct !');
else
alert('Pseudo incorrect ;-(');

III - Utilisation des regex pour remplacement

III.1 - Remplacement basique et flags

En JavaScript, vous pouvez utiliser la fonction replace() sur les chaînes de caractères. Celle-ci prend en paramètres une regex, et la valeur de remplacement.

Prenons un exemple simple :

var message = 'Salut Michel';
alert(message.replace(/Michel/, 'Antoine');

Si vous exécutez ce code, vous aurez un beau message "Salut Antoine". En effet, la fonction replace() remplace l'occurrence de la regex par la valeur indiquée. Ici, elle remplace le "Michel" par un "Antoine"

Maintenant, si vous faites :

var message = 'Salut Michel ! Comment vas-tu, Michel ?';
alert(message.replace(/Michel/, 'Antoine');

Vous aurez un message "Salut Antoine ! Comment vas-tu, Michel ?". En effet, nous n'avons pas dit à la regex "remplace TOUTES les occurrences de Michel par Antoine". Il faut ajouter à la regex un petit caractère nommé flag (drapeau, en franÃ§ais), pour lui dire de faire le remplacement pour TOUTES les occurrences.

Le flag résolvant notre problème est le flag g. Les flags doivent être inscrits après la slash (/) fermant, comme ceci :

var message = 'Salut Michel ! Comment vas-tu, Michel ?';
alert(message.replace(/Michel/g, 'Antoine');

Et Ã§a fonctionne !

Un autre flag est le flag i. Il permet d'indiquer à la regex d'être insensible à la casse, comme ceci :

var message = 'salut MICHEL';

if(message.match(/michel/)) {} // ne fonctionne pas
if(message.match(/michel/i)) {} // fonctionne !

Cela peut également vous permettre de ne pas avoir à écrire de [a-z] dans votre regex :-) !

Le dernier flag que je vous montrerais dans ce cours est m. Il permet de dire "teste-moi Ã§a sur chaque ligne" :

var message = 'Salut Michel !\nSalut Jean !\nSalut Henri !';

if(message.match(/^Salut Jean/)) {} // ne fonctionne pas, car la chaîne ne commence pas par "Salut Jean"
if(message.match(/^Salut Jean/m)) {} // fonctionne, car une des lignes commence par "Salut Jean"

Pratique, hein ?

Evidemment, utiliser des flags réduira la vitesse d'exécution de la regex, mais cela ne se ressentira pas pour un humain (exécuter une regex comme celle-ci prends moins d'un millième de seconde pour votre ordinateur !).

III.2 - Remplacement avec données de regex

On va maintenant voir comment extraire des données d'une chaîne. Par exemple, vous avez une phrase. Celle-ci contient un code à 4 chiffres, que vous aimeriez bien connaître. On peut d'ores et déjà  vérifier que le code est bien présent comme ceci :

/([0-9]){4}/

Mais comment le récupérer ? On va en fait utiliser un petit symbole : $. Lorsqu'il est suivi d'un chiffre, il permet de récupérer une donnée. Par exemple, dans la regex :

/(Salut|Bonjour|Hey) (Henri|Martin)/

$1 vaudra soit "Salut", soit "Bonjour", soit "Hey".
$2 vaudra soit "Henri", soit "Martin"

Dans "Salut Martin", avec cette regex, $1 vaudra "Salut" et $2 vaudra "Martin" ! Pour récupérer ces valeurs, il faut utiliser la fonction match(), comme ceci :

var message = 'Tiens, salut Martin !';
var match = message.match(/(salut|bonjour) (Henri|Martin)/);

Si vous regardez de plus près le contenu de match, vous pouvez voir que c'est un tableau, contenant :

- match[0] : toute l'occurrence (ici, "salut Martin") ;

- match[1] : vaut $1 (ici, "salut") ;

- match[2] : vaut $2 (ici, "Martin").

S'il y avait eu un $3, il aurait été stocké dans match[3], et ainsi de suite !

III.3 - Remplacement par fonction

Sachez qu'on peut également passer autre chose qu'une chaîne de caractères comme second paramètre à la fonction replace, on peut aussi passer une fonction ! Ã‡a s'écrit comme Ã§a :

var message = 'salut henri';
alert(message.replace(/salut/g, function() {
return 'bonjour';

Ce code aura pour effet de remplacer tous les "salut" par des "bonjour" !

Et on peut faire encore mieux !

var message = 'hey martin';
alert(message.replace(/(salut|hey)/g, function() {
return 'bonjour';

Là, Ã§a remplacera tous les "salut" et "hey" par des "bonjour" ! Et pour récupérer les données $1, $2 etc., on fait :

var message = 'tiens, salut martin !';
message.replace(/(salut|hey) (henri|martin)/g, function(match, $1, $2) {
console.log(match, $1, $2);
});

Et que s'affiche-t-il dans la console ? Un tableau :-O ! En premier, le match, soit 'salut martin', ensuite $1, soit 'salut' et $2 'martin' ! On peut par exemple faire ceci :

var message = 'tiens, salut martin !';
alert(message.replace(/(salut|hey) (henri|martin)/g, function(match, $1, $2) {
return 'bonjour mon bon ' + $2;
}));

Ce qui donnera 'tiens, bonjour mon bon martin !' (pas très original, mais bon) !

IV - T.P. final

1) Sujet

Vous allez devoir créer une regex capable de censurer les mots "le code est " suivi d'un code composé de chiffres et "le mot de passe est " suivi d'un mot composé de lettres. Je vous laisse choisir entre utiliser une fonction ou non pour le remplacement.

Prêt ? Allez-y !

2) Corrigé

On commence par créer la regex ? On doit remplacer toutes les occurrences de "le code est xxxx" et "le mot de passe est xxxx". Donc pour simplifier, on va faire deux regex !

/le code est ([0-9]+)/i
/le mot de passe est ([a-z]+)/i

Je rajoute un flag i pour ne pas que "le CODE est 1234" échappe à la regex. ;-)
Maintenant, on va effectuer le remplacement : on doit supprimer toutes ces occurrences, soit :

var message = 'salut, le code est 4568 !\nhey, le mot de passe est maSLF !';

message = message.replace(/le code est ([0-9]+)/i, '');
message = message.replace(/le mot de passe est ([a-z]+)/i, '');

alert(message);

Et voilà le travail ! Maintenant, vous maîtrisez les regex JavaScript (PCRE) sur le bout des doigts0 ;-)

V - Remerciements

...