vendredi 12 novembre 2021
Ce billet a été vu pour la première fois sur le blog de Synbioz le 12 November 2021 sous licence CC BY-NC-SA.

Des regex qui ont la classe !

Parlons regex ! Expressions rationnelles. Alors oui, les plus assidus me feront remarquer qu’on a déjà abordé ce thème à plusieurs reprises ; on a abordé le sujet des quantificateurs, des groupes de capture et on a même joué avec les emojis ! Mais il reste encore des aspects de ce fabuleux outil que sont les regex à aborder, et aujourd’hui je vous propose de nous pencher sur les classes de caractères.

Classes de caractères

Une classe de caractère, c’est tout simplement un ensemble de caractères manipulé comme un tout ; généralement parce qu’ils partagent un trait commun. Au sein d’une expression rationnelle, on note une classe de caractère entre crochets, comme ceci :

Moteur !

Il existe différentes implémentations de moteurs de regex, dits moteurs NFA piloté par le motif ou DFA piloté par l’entrée. Suivant le langage et l’environnement dans lequel vous évoluez, de petites différences pourront être constatées. En l’occurrence, tous les moteurs ne supportent pas les mêmes classes de caractères prédéfinies.

Le moteur le plus complet à ce jour étant celui de Perl. Celui-ci permet, par exemple, de rechercher le mot le plus long d’une chaîne de caractères à l’aide de boundaries, possessive quantifiers, positive lookahead, negative lookahead, positive lookbehind… on a sorti tout l’attirail !

/\b(\w++)(?=(.*))(?!(.*\W)\b((?<=(?=(?=\1\2$)(?:(?=\w*+\3(\5?+\w))\w)++\b|(?4)).)))/

Aïe, ça pique !

Un manque de standardisation

Les classes prédéfinies sont fort utiles, seulement elles sont peu portables du fait du manque de standardisation entre les différents moteurs de regex. Voici un petit aperçu.

Vim JS Ruby, Elixir, PHP ASCII Description
    [[:ascii:]] [\x00-\x7F] Caractères ASCII
    [[:alnum:]] A-Za-z0-9 Caractères alphanumériques
\w \w \w ou [[:word:]] A-Za-z0-9_ Caractères alphanumériques, et « _ »
\W \W \W ou [^[:word:]] ^A-Za-z0-9_ Caractères ne composant pas les mots
\a   [[:alpha:]] A-Za-z Caractères alphabétiques
\s   [[:blank:]] \t Espace et tabulation
\\< \\> \b \b ou [[:<:]] [[:>:]] (?<=\W)(?=\w)│(?<=\w)(?=\W) Positions de début et fin de mots
  \B \B ou [^[:<:]] [^[:>:]] (?<=\W)(?=\W)│(?<=\w)(?=\w) Positions ni en début ni en fin de mot
    [[:cnrtl:]] \x00-\x1F\x7F Caractères de contrôle
\d \d \d ou [[:digit:]] 0-9 Chiffres décimaux
\D \D \D ou [^[:digit:]] ^0-9 Autre qu’un chiffre décimal
    [[:graph:]] \x21-\x7E Caractères visibles
\l   [[:lower:]] a-z Lettres en minuscule
\p   [[:print:]] \x20-\x7E Caractères imprimables
    [[:punct:]] ][!"#$%&'()\*+,./:;<=>?@\^_{│}~- Caractères de ponctuation
\_s \s \s ou [[:space:]] \t\r\n\v\f Caractères d’espacement
\S \S \S ou [^[:space:]] ^ \t\r\n\v\f Autre qu’un caractère d’espacement
  \v \v   Caractère d’espacement vertical
    \V   Autre qu’un caractère d’espacement vertical
\u   [[:upper:]] A-Z Lettres capitales
\x \x \h ou [[:xdigit:]] A-Fa-f0-9 Chiffres hexadécimaux
    \H ou [^[:xdigit:]]   Autre qu’un chiffre hexadécimal
  \A \A   Début de chaîne de caractère
  \z \z   Fin de chaîne de caractère

Propriétés Unicode

Mais les classes de caractères ne se limitent pas à celles listées ci-dessus. Il est en effet possible de tirer profit des propriétés Unicode. Voici quelques exemples pour toucher du doigt le potentiel de ces classes.

Jeux de caractères

Il est par exemple possible de rechercher n’importe quel caractère grec :

/\p{Greek}/
2ΠR valent mieux qu'un caillou
 ^

Symboles monétaires

Ou encore, de retrouver les symboles monétaires dans une chaîne :

/\p{Sc}/g
Vous pouvez payer en €uro en £ivre ou en ¥en. Et même en ₿itcoin !
                     ^       ^           ^               ^

Tirets de ponctuation

Voire de détecter n’importe quel tiret de ponctuation :

/\p{Pd}/g
Vous êtes plutôt trait d'union (‐ U+2010), signe moins (- U+002D), tiret demi-cadratin (– U+2013), tiret cadratin (— U+2014) ou tiret numérique (‒ U+2012) ?
                                ^                       ^                               ^                          ^                             ^

Exclusion

Il est même possible d’exclure une sous-classe. Par exemple, voici comment retrouver tous les caractères de ponctuation et symboles, excepté les tirets :

/(?!\p{Pd})[\p{P}\p{S}]/g
schöner co-operative two_words!@#$%^
                        ^     ^^^^^^

Il existe près de 40 de ces propriétés Unicode ! Si la curiosité vous pique, je vous invite à aller faire un tour du côté de la documentation d’Erlang traitant de ce sujet.

À suivre…

Dans un prochain article, je vous présenterai un cas concret de non portabilité d’une expression rationnelle et comment j’ai dû ruser pour arriver à mes fins !