30 avril 2020

Quantificateurs +, *, ? et {n}

Considérons que nous avons une chaîne de caractères +7(903)-123-45-67 et que nous voulons trouver tous les nombres dedans. Mais contrairement à avant nous ne voulons pas seulement trouver les chiffres mais les nombres en entier: 7, 903, 123, 45, 67.

Un nombre est une séquence de 1 ou plus chiffres \d. Pour marquer la quantité dont nous avons besoin, nous pouvons ajouter un quantificateur.

Quantité {n}

Le quantificateur le plus simple est un nombre entre accolades: {n}.

Un quantificateur est attaché à un caractère (ou une classe de caractère, ou un jeu [...], etc) et spécifie la quantité dont nous avons besoin.

Il a quelques formes avancées, comme par exemple:

Le nombre exact: {5}

\d{5} indique exactement 5 chiffres, identique à \d\d\d\d\d.

L’exemple ci-dessous recherche un nombre à 5 chiffres:

alert( "I'm 12345 years old".match(/\d{5}/) ); //  "12345"

Nous pouvons ajouter \b pour exclure les nombres plus longs: \b\d{5}\b.

La portée: {3,5}, correspond de 3 à 5 fois

Pour trouver les nombres avec de 3 à 5 chiffres nous pouvons mettre les limites entre accolades: \d{3,5}

alert( "I'm not 12, but 1234 years old".match(/\d{3,5}/) ); // "1234"

Nous pouvons retirer la limite haute.

Une regexp \d{3,} cherche donc une séquence de chiffres d’une longueur de 3 ou plus:

alert( "I'm not 12, but 345678 years old".match(/\d{3,}/) ); // "345678"

Retournons à la chaîne de caractères +7(903)-123-45-67.

Un nombre est une séquence de un ou plus chiffres à la suite. Donc la regexp est \d{1,}:

let str = "+7(903)-123-45-67";

let numbers = str.match(/\d{1,}/g);

alert(numbers); // 7,903,123,45,67

Abréviations

Il y a des abréviations pour les quantificateur les plus utilisés:

+

Signifie “un ou plus”, identique à {1,}.

Par exemple, \d+ cherche les nombres:

let str = "+7(903)-123-45-67";

alert( str.match(/\d+/g) ); // 7,903,123,45,67
?

Signifie “zéro ou plus”, identique à {0,1}. En d’autres termes, il rend le symbole optionnel.

Par exemple, le pattern ou?r cherche o suivi de zéro ou un u, puis r.

Donc, colou?r trouve color et colour:

let str = "Should I write color or colour?";

alert( str.match(/colou?r/g) ); // color, colour
*

Signifie “zéro ou plus”, identique à {0,}. C’est-à-dire que le caractère peut être répété n’importe quel nombre de fois ou bien être absent.

Par exemple, \d0* cherche un chiffre suivi de n’importe quel nombre de zéros (plusieurs ou aucun):

alert( "100 10 1".match(/\d0*/g) ); // 100, 10, 1

Comparé à + (un ou plus):

alert( "100 10 1".match(/\d0+/g) ); // 100, 10
// 1 n'est pas trouvé, puisque 0+ nécessite au moins un zéro

Plus d’exemples

Les quantificateurs sont utilisés très souvent. Ils servent de “bloc de construction” principal pour les expressions régulières complexes, regardons d’autres exemples.

Regexp pour fractions décimales (un nombre à virgule flotante): \d+\.\d+

En action:

alert( "0 1 12.345 7890".match(/\d+\.\d+/g) ); // 12.345

Regexp pour une “balise HTML d’ouverture sans attributs”, comme <span> ou <p>.

  1. La plus simple: /<[a-z]+>/i

    alert( "<body> ... </body>".match(/<[a-z]+>/gi) ); // <body>

    Cette regexp cherche le caractère '<' suivi par une ou plusieurs lettres Latin, puis '>'.

  2. Amélioré: /<[a-z][a-z0-9]*>/i

    Conformément au standard, Le nom d’une balise HTML peut avoir un chiffre à n’importe quel endroit à l’exception de la première position, comme <h1>.

    alert( "<h1>Hi!</h1>".match(/<[a-z][a-z0-9]*>/gi) ); // <h1>

Regexp “balise HTML d’ouverture ou de fermeture sans attributs”: /<\/?[a-z][a-z0-9]*>/i

Nous avons ajouté un slash optionnel /? près du début du pattern. Nous avons dû l’échapper avec un backslash, sinon Javascript aurait pensé que c’était la fin du pattern.

alert( "<h1>Hi!</h1>".match(/<\/?[a-z][a-z0-9]*>/gi) ); // <h1>, </h1>
Pour rendre une regexp plus précise, nous devons souvent la rendre plus complexe

Vous pouvez voir une règle commune dans tous ces exemples: plus une expression régulière est précise – plus elle est longue et complexe.

Par exemple, pour des balises HTML nous pourrions utiliser une regexp plus simple: <\w+>. Mais comme HTML a des restrictions plus strictes pour les noms de balise, <[a-z][a-z0-9]*> est plus fiable.

Pouvons nous utiliser <\w+> ou avons nous besoin de <[a-z][a-z0-9]*>?

Dans la vrai vie les deux variantes sont acceptables. En fonction de la tolérance que nous avons vis-à-vis des sélections “en trop” et la difficulté que l’on a de les retirer des résultats par d’autres moyens.

Exercices

importance: 5

Créer une regexp pour trouver une ellipse: 3 (ou plus?) points à la suite.

Vérifiez:

let regexp = /votre regexp/g;
alert( "Hello!... How goes?.....".match(regexp) ); // ..., .....

Solution:

let regexp = /\.{3,}/g;
alert( "Hello!... How goes?.....".match(regexp) ); // ..., .....

Notez que le point est un caractère spécial, nous devons donc l’échapper et l’insérer comme ceci \..

Créez une regexp pour trouver les couleurs HTML écrites comme #ABCDEF: d’abord # puis 6 caractères hexadécimaux.

Exemple d’utilisation:

let regexp = /...votre regexp.../

let str = "color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2 #12345678";

alert( str.match(regexp) )  // #121212,#AA00ef

P.S. Dans cette tâche nous n’avons pas besoin des autres formats de couleur comme #123 ou rgb(1,2,3) etc.

Nous devons chercher # suivi de 6 caractères hexadécimaux.

Un caractère hexadécimal est défini comme [0-9a-fA-F]. Ou si nous utilisons le flag i, juste [0-9a-f].

Nous pouvons ensuite rechercher 6 d’entre eux en utilisant le quantificateur {6}.

Résultat, nous avons la regexp: /#[a-f0-9]{6}/gi.

let regexp = /#[a-f0-9]{6}/gi;

let str = "color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2"

alert( str.match(regexp) );  // #121212,#AA00ef

Le problème est qu’elle trouve la couleur dans des séquences plus longues:

alert( "#12345678".match( /#[a-f0-9]{6}/gi ) ) // #123456

Pour règler ceci nous pouvons ajouter \b à la fin:

// color
alert( "#123456".match( /#[a-f0-9]{6}\b/gi ) ); // #123456

// not a color
alert( "#12345678".match( /#[a-f0-9]{6}\b/gi ) ); // null
Carte du tutoriel

Commentaires

lire ceci avant de commenter…
  • Si vous avez des améliorations à suggérer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de préciser.
  • Pour insérer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes -- enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen…)