Dans cet article, nous aborderons différentes méthodes qui fonctionnent en profondeur avec des expressions rationnelles (regexps).
str.match(regexp)
La méthode str.match(regexp)
trouve les correspondances de l’expression rationnelle regexp
dans la chaîne de texte str
.
Elle dispose de 3 options :
-
si l’expression rationnelle n’à pas de marqueur
g
, alors seul la première correspondance est renvoyée sous la forme d’un tableau avec le groupe capturé et ses propriétés : index (indice de la correspondance), et input (chaîne d’entrée équivalent à str):let str = "I love JavaScript"; let result = str.match(/Java(Script)/); alert( result[0] ); // JavaScript (correspondance exacte) alert( result[1] ); // Script (premier groupe capturant) alert( result.length ); // 2 // Additional information: alert( result.index ); // 7 (indice de la chaîne de caractère où à été trouvée la correspondance) alert( result.input ); // I love JavaScript (chaîne sur laquelle a été effectuée la recherche)
-
Si la
regexp
dispose d’un marqueurg
, alors elle retourne un tableau de toutes les correspondances de texte, sans capturer les groupes ou les autres propriétés.let str = "I love JavaScript"; let result = str.match(/Java(Script)/g); alert( result[0] ); // JavaScript alert( result.length ); // 1
-
S’il n’y a pas de correspondance, qu’il y ait un marqueur
g
ou non,null
est renvoyé.C’est une nuance importante. Si il n’y a pas de correspondance, nous ne récupérons pas de tableau vide, mais
null
. Il n’est pas rare de faire une erreur en oubliant ce détail, e.g.:let str = "I love JavaScript"; let result = str.match(/HTML/); alert(result); // null alert(result.length); // Error: Cannot read property 'length' of null
Si nous voulons que le résultat soit un tableau, nous pouvons écrire de cette façon:
let result = str.match(regexp) || [];
str.matchAll(regexp)
La méthode str.matchAll(regexp)
est une variante “améliorée” de str.match
.
Elle est principalement utilisée pour rechercher toutes les correspondances au sein de chaque groupe.
Il y a 3 différences avec match
:
- Elle retourne un objet iterable avec les correspondances au lieu d’un tableau. Nous pouvons le transformer en un tableau classique en utilisant la méthode
Array.from
. - Toutes les correspondances sont retournées dans un tableau incluant les groupes capturants (sous le même format que
str.match
sans le marqueurg
). - S’il n’y a aucun résultat, il renvoie un objet itérable vide au lieu de
null
.
Exemple d’utilisation:
let str = '<h1>Hello, world!</h1>';
let regexp = /<(.*?)>/g;
let matchAll = str.matchAll(regexp);
alert(matchAll); // [object RegExp String Iterator], pas un tableau, mais un itérateur
matchAll = Array.from(matchAll); // maintenant un tableau
let firstMatch = matchAll[0];
alert( firstMatch[0] ); // <h1>
alert( firstMatch[1] ); // h1
alert( firstMatch.index ); // 0
alert( firstMatch.input ); // <h1>Hello, world!</h1>
Si nous utilisons for..of
pour boucler sur les résultats de matchAll
, alors il n’est pas nécessaire d’utiliser Array.from
.
str.split(regexp|substr, limit)
Divise la chaîne de caractères en utilisant la regexp (ou une sous-chaîne de caractères) comme délimiteur.
Nous pouvons utiliser split
avec une chaîne de caractères comme ceci :
alert('12-34-56'.split('-')) // array of ['12', '34', '56']
Mais nous pouvons aussi diviser une chaîne de texte en utilisant une expression rationnelle:
alert('12, 34, 56'.split(/,\s*/)) // array of ['12', '34', '56']
str.search(regexp)
La méthode str.search(regexp)
renvoie l’indice du premier motif correspondant, ou -1
si aucune correspondance n’est trouvée:
let str = "A drop of ink may make a million think";
alert( str.search( /ink/i ) ); // 10 (indice du premier motif correspondant)
Limitation importante: search
renvoie uniquement la première correspondance.
Si nous avons besoin de l’indice ou de plus de correspondances, nous devrions utiliser d’autres méthodes, comme les trouver tous avec str.matchAll(regexp)
.
str.replace(str|regexp, str|func)
Il s’agit d’une méthode générique pour chercher et remplacer une chaîne de caractères, l’une des plus utiles. Le couteau suisse pour chercher et remplacer.
Nous pouvons l’utiliser sans regexps, pour chercher et remplacer une sous-chaîne de caractères:
// replace a dash by a colon
alert('12-34-56'.replace("-", ":")) // 12:34-56
Toutefois, il y a un piège.
Quand le premier argument de replace
est une chaîne de caractères, elle ne remplace que la première occurence.
Vous pouvez constater dans l’exemple ci-dessous que seul le premier "-"
est remplacé par ":"
.
Pour trouver tous les traits d’unions, nous devons utiliser non pas le caractère "-"
, mais une expression rationnelle /-/g
, avec obligatoirement le marqueur g
;
// remplace tous les tirets par deux-points
alert( '12-34-56'.replace( /-/g, ":" ) ) // 12:34:56
Le second argument est une chaîne de caractères de remplacement. Nous pouvons utiliser des caractères spéciaux dedans :
Symbols | Action in the replacement string |
---|---|
$& |
insère la chaine de caractère en correspondance |
$` |
Insère la partie de la chaîne de caractère qui précède la sous-chaîne en correspondance |
$' |
insère la partie de la chaîne de caractère qui suit la sous-chaîne en correspondance |
$n |
si n est un nombre à 1 ou 2 chiffres, insère la n-ième chaîne de sous-correspondance entre parenthèses, pour plus de détails voir Groupes capturant |
$<name> |
insère la chaîne de caractère du name correspondant à celui entre parenthèse, pour plus de détails voir Groupes capturant |
$$ |
insère un caractère $ |
Par exemple:
let str = "John Smith";
// inverser le prénom et nom de famille
alert(str.replace(/(john) (smith)/i, '$2, $1')) // Smith, John
Si le contexte nécessite un remplacement “intelligent”, le second argument peut être une fonction.
Elle sera appelée pour chaque correspondance, et la valeur de retour sera insérée comme remplacement.
La fonction est appelée avec des arguments func(match, p1, p2, ..., pn, offset, input, groups)
:
match
– La chaîne de caractère en correspondance,p1, p2, ..., pn
– contenu des groupes capturants (s’il y en a),offset
– indice de la sous-chaîne correspondante,input
– la chaîne de texte initiale,groups
– un objet contenant les groupes nommés.
Si la regexp ne comporte pas de parenthèses, alors la fonction ne contient que 3 arguments: func(str, offset, input)
.
Par exemple, pour convertir les chaînes de caractères correspondantes en majuscule:
let str = "html and css";
let result = str.replace(/html|css/gi, str => str.toUpperCase());
alert(result); // HTML and CSS
Remplace chaque résultat en utilisant son indice dans la chaîne de caractères:
alert("Ho-Ho-ho".replace(/ho/gi, (match, offset) => offset)); // 0-3-6
Dans l’exemple ci-dessous, il y a 2 groupes entre parenthèses. La fonction de remplacement est alors appelée avec 5 arguments: le premier est la correspondance complète, puis chacun des groupes entre parenthèses et enfin (non présent dans l’exemple) l’indice de la correspondance et la chaîne de caractères initiale:
let str = "John Smith";
let result = str.replace(/(\w+) (\w+)/, (match, name, surname) => `${surname}, ${name}`);
alert(result); // Smith, John
Si il y a de nombreux groupes entre parenthèses, il peut être pratique d’utiliser les paramètres du reste pour y accéder:
let str = "John Smith";
let result = str.replace(/(\w+) (\w+)/, (...match) => `${match[2]}, ${match[1]}`);
alert(result); // Smith, John
Ou, si nous utilisons des groupes nommés, alors l’objet groups
est toujours placé en dernier, et nous pouvons l’obtenir de cette façon:
let str = "John Smith";
let result = str.replace(/(?<name>\w+) (?<surname>\w+)/, (...match) => {
let groups = match.pop();
return `${groups.surname}, ${groups.name}`;
});
alert(result); // Smith, John
Les fonctions représentent le pouvoir ultime pour effectuer un remplacement. Elles recupèrent toutes les informations des correspondances, ont accès aux variables externes et sont capable de tout faire.
str.replaceAll(str|regexp, str|func)
This method is essentially the same as str.replace
, with two major differences:
- If the first argument is a string, it replaces all occurrences of the string, while
replace
replaces only the first occurrence. - If the first argument is a regular expression without the
g
flag, there’ll be an error. Withg
flag, it works the same asreplace
.
The main use case for replaceAll
is replacing all occurrences of a string.
Like this:
// replace all dashes by a colon
alert('12-34-56'.replaceAll("-", ":")) // 12:34:56
regexp.exec(str)
La méthode regexp.exec(str)
renvoie une correspondance for regexp
dans la chaîne de caractères str
. À l’inverse de la méthode précédente, elle est appelée sur une regexp et non une chaîne de caractères.
Elle se comporte différement selon que la regexp dispose d’un marqueur g
ou non.
Si g
n’est pas présent, alors regexp.exec(str)
renvoie la première correspondance tel que le ferait str.match(regexp)
. Ce comportement n’apporte rien de nouveau.
Mais si g
est utilisé, alors:
- Un appel à
regexp.exec(str)
renvoie la première correspondance et sauvegarde l’indice situé juste après, accessible via la propriétéregexp.lastIndex
. - l’appel suivant à la fonction commence la recherche depuis l’indice contenu dans
regexp.lastIndex
. La correspondance suivante est renvoyé et l’indice positionné après est sauvegardé dansregexp.lastIndex
. - …Et ainsi de suite.
- Si aucune correspondance n’est trouvée,
regexp.exec
renvoienull
etregexp.lastIndex
est réinitialisé à0
.
Donc, un appel répété à cette fonction renvoie toutes les correspondances l’une après l’autre, utilisant la propriété regexp.lastIndex
pour se souvenir de l’indice courant à partir duquel la recherche est effectuée.
Avant que la méthode str.matchAll
ait été ajoutée à JavaScript, des appels à regexp.exec
étaient utilisés dans une boucle afin d’obtenir toutes les correspondances:
let str = 'More about JavaScript at https://javascript.info';
let regexp = /javascript/ig;
let result;
while (result = regexp.exec(str)) {
alert( `Found ${result[0]} at position ${result.index}` );
// Found JavaScript at position 11, puis
// Found javascript at position 33
}
Cela fonctionne également très bien, bien que sur les navigateurs les plus récents str.matchAll
est généralement plus pratique.
Nous pouvons utiliser regexp.exec
pour rechercher à partir d’un indice donné en réglant manuellement la valeur de lastIndex
.
Par exemple:
let str = 'Hello, world!';
let regexp = /\w+/g; // sans le marqueur "g", la propriété lastIndex est ignorée
regexp.lastIndex = 5; // commence la recherche à partir de la 5ème position (à partir de la virgule)
alert( regexp.exec(str) ); // world
Si la regexp utilise le marqueur y
, alors la recherche s’effectuera à l’indice précis de regexp.lastIndex
, pas plus loin.
Remplaçons le marqueur g
par y
dans l’exemple précédent. Aucune correspondance n’est trouvée, car il n’y a aucun mot à l’indice 5
:
let str = 'Hello, world!';
let regexp = /\w+/y;
regexp.lastIndex = 5; // cherche exactement à l'indice 5
alert( regexp.exec(str) ); // null
C’est pratique dans une situation où nous cherchons uniquement à lire quelque chose au sein d’un texte avec une regexp à un indice spécifique, en occultant le reste.
regexp.test(str)
La méthode regexp.test(str)
vérifie qu’une correspondance existe et renvoie true/false
selon le cas.
Par exemple:
let str = "I love JavaScript";
// Ces deux tests réalisent exactement la même chose
alert( /love/i.test(str) ); // true
alert( str.search(/love/i) != -1 ); // true
Un exemple avec un retour négatif:
let str = "Bla-bla-bla";
alert( /love/i.test(str) ); // false
alert( str.search(/love/i) != -1 ); // false
Si la regexp à le marqueur g
, alors regexp.test
verifiera la propriété regexp.lastIndex
et mettra à jours cette propriété, tout comme regexp.exec
.
On peut donc l’utiliser pour effectuer une recherche à partir d’un indice donnée:
let regexp = /love/gi;
let str = "I love JavaScript";
// commence la recherche à partir de l'indice 10:
regexp.lastIndex = 10;
alert( regexp.test(str) ); // false (pas de correspondance)
Appliquer la même expression rationnelle globale sur différentes entrées peut conduire à de mauvais résultats, car l’appel à regexp.test
modifie la propriété regexp.lastIndex
, par conséquent la recherche sur une autre chaîne de caractères risque d’être lancer à partir d’un autre indice que 0
.
Par exemple, nous appelons ici regexp.test
à deux reprises sur la même chaîne de texte, and le second appel échoue:
let regexp = /javascript/g; // (création d'une nouvelle regexp: regexp.lastIndex=0)
alert( regexp.test("javascript") ); // true (maintenant regexp.lastIndex=10)
alert( regexp.test("javascript") ); // false
C’est exactement parce que regexp.lastIndex
n’est pas 0
lors du second test.
Afin de contourner cela, nous pouvons réinitialiser regexp.lastIndex = 0
avant chaque recherche. Ou, au lieu d’appeler la méthode sur une regexp, nous pouvons utiliser les méthodes de l’objet String str.match/search/...
, qui n’utilisent pas lastIndex
.