Les expressions régulières

Les expressions régulières ou RegEx est une syntaxe conçue pour effectuer des recherches au sein d'un texte. On peut donc rechercher des mots mais aussi des structures de phrase.

La syntaxe en résumé.

rechercher un début ou une fin.

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

    Exemple :

    La réponse doit commencer par Le soleil La RegEx est donc ^Le soleil

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

    Exemple :

    La réponse doit finir par le soleil La RegEx est donc le soleil$

    Exemple :

    Si la réponse de l'élève est "les gens" et qu'on cherche le mot les alors les RegEx suivantes fonctionnent les et ^les mais pas ^les$ ou même les$

Quantifier les caractères.

  • . le point signifie n'importe quel caractère.
  • * le caractère, ou le contenu de la parenthèse précédent peut être présent 0, 1 ou plusieurs fois.
  • + le caractère, ou le contenu de la parenthèse précédent peut être présent 1 ou plusieurs fois.
  • ? le caractère, ou le contenu de la parenthèse précédent peut être présent ou pas.

Exemples :

  • accepter affiche ou afiche, dans la RegEx mettre aff?iche
  • accepter si le mot commence par affich et se termine par es (affiches, affichages), dans la RegEx mettre affich\w*es

Les caractères spéciaux réservés à la syntaxe.

  • \d un caractère qui représente un chiffre.
  • \D un caractère qui n'est pas un chiffre.
  • \w un caractère qui est alpha numérique A-Z ou a-z ou 0-9 les caractères accentués n'en font pas partie.
  • \W un caractère qui ne représente pas \w

Exemples :

  • accepter affiche ou afiche, dans la RegEx mettre aff?iche
  • accepter si le mot commence par affich et se termine par es (affiches, affichages), dans la RegEx mettre affich\w*es

Les classes de caractères.

  • Accepter lorsque le caractère fait partie d'une liste. La liste est entre crochet [liste de caractères permis].

    Exemples :

    • accepter un e ou un e accentué, dans la RegEx mettre [eéèê]tre on accepte donc dans la réponse les mots etre, étre, ètre et être
  • Accepter si le caractère ne fait pas partie d'une liste. [^liste de caractères non permis].

    Exemples :

    • accepter le nombre 10 non précédé d'un chiffre et non suivi d'un chiffre, dans la RegEx mettre [^0-9]10[^0-9]

Remarque :

Entre les crochets, le caractère - signifie de .... à .... exemple [0-9] chiffre de 0 à 9.

Les alternatives.

Les alternatives sont séparées par le caractère | c'est un ou logique.

Exemples :

accepter force ou action mécanique, dans la RegEx mettre force|action mécanique

Les parenthèses.

Les parenthèses permettent de chercher un critère complexe ou d'isoler des alternatives.

Exemples :

accepter force ou action mécanique suivi, plus loin dans la réponse, du mot gravité , dans la RegEx mettre ( force | action mécanique ).* gravité. Les espaces ont leur importance ici.

La syntaxe en détail

La construction d'une expression régulière dépend uniquement de la connaissance des opérateurs d'expression régulière et caractères spéciaux, ainsi que des modifieurs globaux.

Les opérateurs, intervalles et groupes

Les opérateurs, intervalles et groupes

En regroupant des éléments dans une expression, on peut appliquer des opérateurs logiques. Ajoutant à cela les intervalles, il devient possible d'exprimer en peut de lettres un ensemble de règles.

Le point

Le point désigne tout caractère dans le texte à comparer. Sauf le code de fin de ligne.

Groupes

()

Les parenthèses désignent un groupe de rappel, trouve l'élément entre parenthèse et le mémorise pour le restituer dans le tableau résultat ou dans les variables de l'objet RegExp.
Le masque (.) désigne un caractère quelconque. Associé à l'opérateur +, donc (.)+ cela signifie un caractère quelconque au moins, donc un seul caractère ou une chaîne de caractères.

Par exemple (ari) permet de retrouver "ariane", ou "baril", et mais "carquois" n'est pas retenu. Puis ari est mémorisé.

(?:x)

Parenthèses non capturantes. On recherche l'élément x, mais il n'est pas mémorisé et n'apparaît pas dans le résultat pour la méthode qui retourne un tableau. Ni dans les variables internes.

[]

Les crochets désignent un groupe alternatif. On recherche l'un ou l'autre des éléments dans la liste.
Dans le cas ou l'on recherche [abc], alors "ariane", "baril", "corail" peuvent correspondre (si l'on teste la première lettre).

Intervalle

-

Le symbole tiret désigne entre deux lettres ou chiffres désigne un intervale.
Exemples:
a-z liste des lettres minuscules. N'importe quelle lettre dans la liste peut correspondre.
A-Z liste des majuscules.
0-9 liste des chiffres.

Opérateurs de parties

Ces symboles servent à désigner une partie spéficifique des textes à comparer avec l'expression régulière.

^

Spécifie que l'élément qui suit, caractère ou groupe, doit être placé au début du texte pour qu'il corresponde à la recherche. Si le masque est /^e/ le texte "enfin" est retenu et pas "terme".
Dans le cas d'un texte en plusieurs lignes, avec le modifieur "m" en option, cela s'applique au début de chaque ligne.

$

Spécifie que l'élément précédent, caractère ou groupe doit terminer la fin du texte. Si le masque est /e$/ le texte "enfin" n'est pas retenu, mais "ariane" le serait .
Dans le cas d'un texte en plusieurs lignes, avec le modifieur "m" en option, cela s'applique à la fin de chaque ligne.

?

L'élément précédant peut être présent ou non.
a? signifie qu'il peut y avoir une lettre a ou aucune. Cela permet de passer le caractère lorsqu'il est présent pour appliquer la suite de l'expression régulière sur la partie du texte qui vient après.

Opérateurs de quantité

+

Il doit y avoir ou moins un élément de la lettre ou du groupe précédant le symbole.

Exemples:
a+ il doit y avoir un a ou plusieurs.
[abc]+ il doit y avoir un a ou un b ou un c ou plusieurs de ces mêmes lettres (pas une combinaison).

*

Il peut y avoir un nombre indéterminé d'occurence du texte précédent, ou aucune.

{ n }

n représente un nombre entier quelconque. C'est le nombre d'occurences que l'on recherche.
Exemple:
a{2} On recherche une chaîne qui contient "aa".

{ x, y }

x et y représentent deux nombres entiers positifs. Il y aura au moins x occurences et au plus y occurences.
Par exemple { 2, 3 } On recherche deux ou trois occurences d'une chaîne.

Opérateurs logiques

x | y

La barre est l'opérateur OU inclusif.
Exemple: (abc | def)
On recherche la chaîne qui contient abc ou def (ou les deux).

[^]

Le symbole "^" quand il est entre crochets ne désigne pas le début d'un ensemble mais l'exclusion de cet ensemble.
Exemple:
[^xyz]
L'expression représente toutes les lettres sauf x, y ou z.

Opérateurs conditionnels

x(?=y)

Le texte correspond quand x est suivi par y.
Exemple:
moi(?=elle)
Quand moi est suivi directement par elle dans le texte, l'expression est satisfaite. Pour conserver les deux chaînes dans le tableau résultat, on écrira: moi(?=(elle))

Exemple:
(0-9)+(?=\.)(0-9)+
Représente un nombre décimal: suite de chiffres, point, et décimales. Cela peut s'écrire plus simplement: \d+\.\d+

x(?!y)

Le texte x correspond s'il n'est pas suivi par y.
Pour représenter un nombre entier on écrirait:
[0-9]+(?!\.) mais [0-9]+ serait plus simple.

Note importante

Dans une chaîne de caractères, le code "\" doit être doublé. Par exemple on écrira \\d pour représenter le symbole \d, un digit. Ce n'est pas le cas quand on entre l'expression régulière dans un formulaire, ni dans la forme litérale:

/\d+/

(?<=y)x

Correspond à x seulement si x est précédé par y. C'est ce qu'on appelle une recherche arrière (lookbehind).
Ainsi /(?<=Jack)Sprat/ correspond à "Sprat" seulement s'il est précédé de "Jack".
/(?<=Jack|Tom)Sprat/ correspond à "Sprat" seulement s'il est précédé de "Jack" ou "Tom".
Toutefois, "Jack" et "Tom" ne feront pas partie de la correspondance.

(?<!y)x

Correspond à x uniquement si x n'est pas précédé par y (parfois appelée en anglais negated lookbehind).
Ainsi, (?<!non\W?)miscibles?

  • Dans la phrase : « Ces deux liquides sont miscible.» la correspondance sera trouvée car le mot miscible n'est pas précédé du mot "non"
  • Dans les phrases :
    • « Ces deux liquides sont nonmiscible. »
    • « Ces deux liquides sont non-miscible. »
    • « Ces deux liquides sont non miscible. »
    ne trouve aucune correspondance car le mot "miscible" est précédé du mot "non" qu'il soit attaché ou non au mot "miscible".

Les quantificateurs gourmands et fainéants

Les quantificateurs gourmands et fainéants

Que sont les quantificateurs ?

Commençons par un peu de grammaire et de vocabulaire, de quoi parlons-nous ? Et comment cela s’articule-t-il ?

Les quantificateurs permettent de préciser dans une regex le nombre d’occurrences attendues pour un caractère ou un groupe de caractères donné. Pour cela, des caractères ayant une signification spécifique sont mis à notre disposition. Il s’agit du point d’interrogation ?, de l’étoile *, du signe plus +, ainsi que des accolades {}.

Voici leur signification :

  • a? zéro ou une occurrence de a
  • a* zéro ou plusieurs occurrences de a
  • a+ une ou plusieurs occurrences de a
  • a{3} exactement trois occurrences de a
  • a{3,} trois occurrences de a ou davantage
  • a{3,6} trois à six occurrences de a

Le gourmand

Par défaut, les quantificateurs sont gourmands (ou greedy en anglais), c’est-à-dire qu’ils maximisent la concordance. Dit autrement, tant qu’un caractère correspond au motif recherché, il sera consommé. Prenons un exemple.

Supposons que nous faisons une recherche dans la chaîne andouillette AAAAA à l’aide de cette regex :

/(.*)A/ 

Voici ce qui en résulterait :

Concordance complète (0-18) : andouillette AAAAA Groupe 1             (0-17) : andouillette AAAA 

Expliquons-nous. Nous recherchons n’importe quel caractère, un nombre indéterminé de fois .*, c’est là notre groupe 1 ; suivi pour finir du caractère A. Notre quantificateur étant gourmand, il va consommer tous les caractères de la chaîne, sauf le dernier A de façon à ce qu’il puisse y avoir concordance.

Le fainéant

Il est possible d’obtenir le comportement inverse. C’est-à-dire minimiser la concordance. Pour cela, il nous faut suffixer notre quantificateur d’un point d’interrogation *?. On parle alors de quantificateur fainéant ou lazy.

Prenons le même exemple, mais en le comparant cette fois à cette regex :

/(.*?)A/ 
Concordance complète (0-14) : andouillette A Groupe 1             (0-13) : andouillette 

À présent, sitôt qu’un sous-ensemble de notre chaîne de caractère fait l’affaire, la concordance s’arrête. Notre quantificateur nous empêche d’en parcourir davantage.

Cet exemple peut s’avérer très artificiel. Voyons un cas plus concret. Disons que vous souhaitez récupérer l’ensemble des attributs d’une balise HTML. Par exemple, celle-ci :

<a href="https://www.synbioz.com">Synbioz</a> 

Votre première idée serait peut-être d’écrire un regex comme celle-ci :

/<a (.*)>.*</a>`/ 
Concordance complète (0-45) : <a href="https://www.synbioz.com">Synbioz</a> Groupe 1             (3-33) : href="https://www.synbioz.com" 

Effectivement, cela fonctionne et le premier groupe contiendra bien la sous-chaîne recherchée href="https://www.synbioz.com". Mais on peut obtenir une regex plus concise grâce à un quantificateur fainéant :

/<a (.*?)>/ 
Concordance complète (0-34) : <a href="https://www.synbioz.com"> Groupe 1             (3-33) : href="https://www.synbioz.com" 

Notons que dans ce cas très précis, l’approche la plus efficace, mais dont l’intention est bien moins déchiffrable, reste l’utilisation d’une classe de caractères négative telle [^>], c’est-à-dire « tout sauf un chevron > » — comme ceci :

/<a ([^>]*)>/ 
Concordance complète (0-34) : <a href="https://www.synbioz.com"> Groupe 1             (3-33) : href="https://www.synbioz.com" 

Il y a toujours plusieurs moyens d’arriver à ses fins à l’aide d’une regex, le tout étant de savoir quelle est votre priorité : la performance ou la compréhensibilité.

Le possessif

Le dernier modificateur dont nous disposons est le possessif (ou possessive en anglais). Le possessif, qui se note *+, est gourmand et empêche tout retour en arrière. Autrement dit, tout caractère consommé ne pourra être relâché pour tenter de trouver une concordance.

Si l’on reprend notre « andouillette AAAAA » comme exemple avec notre nouvelle regex…

/(.*+)A/ 

…nous n’aurions alors aucune concordance ! Pour bien saisir, il faut comprendre le fonctionnement interne d’une recherche de concordance. Le moteur de regex commencera au début de notre chaîne, consommera tous les caractères .*+ puis recherchera un A. Mais il n’y en a plus, car tout a été consommé et notre quantificateur ne restitue aucun caractère consommé pour aider le moteur de regex à trouver une concordance. Qu’à cela ne tienne, notre petit moteur va commencer sa recherche à partir du deuxième caractère… ce qui aura la même issue malheureuse. Il tentera donc à partir du troisième, et ainsi de suite, nous menant toujours dans une impasse, dans l’impossibilité de donner satisfaction à cette regex.

Alors, à quoi cela sert-il, me direz-vous !? L’intérêt d’un quantificateur possessif réside essentiellement dans la performance : il permet d’échouer plus vite en empêchant le moteur de regex d’effectuer certaines permutations.

Les caractères spéciaux

Les caractères spéciaux

Les caractères spéciaux sont introduits par le code d'échappement "\". Dans un littéral (ou un formulaire) mais dans une chaîne, le slash inversé est doublé.

x = /a\r/
x = new RegExp("a\\r")

Celui-ci associé à une lettre représente un code qui ne peut être affiché directement, mais il sert aussi, quand il est associé à un code opérateur, à désigner le caractère plutôt que l'opérateur d'expression régulière:

\n désigne la fin de ligne et non pas la lettre n. 

\* désigne le caractère étoile et non pas l'opérateur d'expression régulière étoile. 
\t  code de tabulation. \v pour une tabulation verticale.
\r  code de retour à la ligne.
\f  code de fin de page.
\s code de séparation quelconque, incluant:espace blanc, tabulation, retour à la ligne, fin de page.
\S tout caractère autre qu'un espace, c'est le contraire de \s.
\d   tout digit, autrement dit tout caractère numérique.  Equivaud à [0-9].
\D  tout caractère non numérique.  Equivaud à [^0-9].  
\w tout caractère alphanumérique. Equivaud à [_A-Za-z0-9].
\W tout caractère autre qu'alphanumérique. C'est le contraire de \w et cela équivaud à  [^_A-Za-z0-9].
\nnnn où nnnn est un nombre entier positif.    
\0  Représente le code 0 dans le fichier binaire (et non le chiffre 0 dans le texte).
\xhh  Où hh est un couple hexadécimal. Représente un code dans le binaire.
\uhhhh  Code hexadécimal sur 4 digits.