Agence web » Actualités du digital » Comment utilisez-vous réellement Regex?

Comment utilisez-vous réellement Regex?

Regex, abréviation d'expression régulière, est souvent utilisé dans les langages de programmation pour faire correspondre des modèles dans des chaînes, rechercher et remplacer, valider des entrées et reformater du texte. Apprendre à utiliser correctement Regex peut faciliter le travail avec du texte.

Syntaxe des regex, expliquée

Regex a la réputation d'avoir une syntaxe horrible, mais il est beaucoup plus facile d'écrire que de lire. Par exemple, voici un regex général pour un validateur de courrier électronique conforme à la RFC 5322:

(?:(a-z0-9!#$%&'*+/=?^_`{|}~-)+(?:.(a-z0-9!#$%&'*+/=?^_`{|}~-)+)*|"(?:(x01-
x08x0bx0cx0e-x1fx21x23-x5bx5d-x7f)|\(x01-x09x0bx0cx0e-x7f))*")
@(?:(?:(a-z0-9)(?:(a-z0-9-)*(a-z0-9))?.)+(a-z0-9)(?:(a-z0-9-)*(a-z0-9))?|((?
:(?:25(0-5)|2(0-4)(0-9)|(01)?(0-9)(0-9)?).){3}(?:25(0-5)|2(0-4)(0-9)|(01)?(0-
9)(0-9)?|(a-z0-9-)*(a-z0-9):(?:(x01-x08x0bx0cx0e-x1fx21-x5ax53-x7f)|
\(x01-x09x0bx0cx0e-x7f))+)))

S'il semble que quelqu'un s'est écrasé le visage contre le clavier, vous n'êtes pas seul. Mais sous le capot, tout ce gâchis est en fait la programmation d'une machine à états finis. Cette machine fonctionne pour chaque personnage, évoluant et correspondant en fonction des règles que vous avez définies. De nombreux outils en ligne afficheront des diagrammes de chemin de fer, montrant le fonctionnement de votre machine Regex. Voici ce même Regex sous forme visuelle:

C'est toujours très déroutant, mais c'est beaucoup plus compréhensible. C’est une machine avec des pièces mobiles qui ont des règles définissant comment tout s’emboîtent. Vous pouvez voir comment quelqu'un a assemblé cela; ce n'est pas seulement un gros globe de texte.

Tout d'abord: utilisez un débogueur Regex

Avant de commencer, à moins que votre Regex soit particulièrement court ou que vous soyez particulièrement compétent, vous devez utiliser un débogueur en ligne lors de son écriture et de son test. Cela facilite la compréhension de la syntaxe. Nous recommandons Regex101 et RegExr, qui offrent tous deux des tests et une référence de syntaxe intégrée.

Comment fonctionne Regex?

Pour l'instant, concentrons-nous sur quelque chose de beaucoup plus simple. Voici un diagramme de Regulex pour une Regex de correspondance par e-mail très courte (et certainement pas conforme à la RFC 5322):

Le moteur Regex démarre à gauche et parcourt les lignes, faisant correspondre les caractères au fur et à mesure. Le groupe n ° 1 correspond à n'importe quel caractère, sauf un saut de ligne, et continuera de faire correspondre les caractères jusqu'à ce que le bloc suivant trouve une correspondance. Dans ce cas, il s'arrête lorsqu'il atteint un @ symbole, ce qui signifie que le groupe # 1 capture le nom de l'adresse e-mail et tout ce qui suit correspond au domaine.

Le Regex qui définit le groupe # 1 dans notre exemple de courrier électronique est:

(.+)

Les parenthèses définissent un groupe de capture, qui indique au moteur Regex d'inclure le contenu de la correspondance de ce groupe dans une variable spéciale. Lorsque vous exécutez une expression régulière sur une chaîne, le retour par défaut est la correspondance entière (dans ce cas, l'e-mail entier). Mais il renvoie également chaque groupe de capture, ce qui rend cette Regex utile pour extraire des noms des e-mails.

La période est le symbole de «Tout caractère sauf la nouvelle ligne». Cela correspond à tout sur une ligne, donc si vous avez transmis cet e-mail, Regex a une adresse comme:

%$#^&%*#%$#^@gmail.com

Cela correspondrait %$#^&%*#%$#^ comme nom, même si c'est ridicule.

Le symbole plus (+) est une structure de contrôle qui signifie «faire correspondre le caractère ou le groupe précédent une ou plusieurs fois». Il garantit que le nom entier correspond, et pas seulement le premier caractère. C'est ce qui crée la boucle trouvée sur le diagramme de chemin de fer.

Le reste du Regex est assez simple à déchiffrer:

(.+)@(.+..+)

Le premier groupe s'arrête quand il frappe le @ symbole. Le groupe suivant démarre alors, qui correspond à nouveau à plusieurs caractères jusqu'à ce qu'il atteigne un caractère point.

Étant donné que des caractères tels que les points, les parenthèses et les barres obliques sont utilisés dans le cadre de la syntaxe dans Regrex, chaque fois que vous souhaitez faire correspondre ces caractères, vous devez les échapper correctement avec une barre oblique inverse. Dans cet exemple, pour correspondre à la période que nous écrivons . et l'analyseur le traite comme un symbole signifiant "correspondre à une période".

Correspondance de caractères

Si vous avez des caractères non contrôlés dans votre Regex, le moteur Regex supposera que ces caractères formeront un bloc correspondant. Par exemple, le Regex:

he+llo

Correspondra au mot «bonjour» avec un nombre quelconque de e. Tous les autres personnages doivent être échappés pour fonctionner correctement.

Regex a également des classes de caractères, qui agissent comme un raccourci pour un ensemble de caractères. Ceux-ci peuvent varier en fonction de l'implémentation de Regex, mais ceux-ci sont standard:

  • . – correspond à tout sauf à la nouvelle ligne.
  • w – correspond à n'importe quel caractère "mot", y compris les chiffres et les traits de soulignement.
  • d – correspond aux nombres.
  • b – correspond aux espaces blancs (c'est-à-dire, espace, tabulation, nouvelle ligne).

Ces trois ont tous des homologues en majuscules qui inversent leur fonction. Par exemple, D correspond à tout ce qui n'est pas un nombre.

Regex a également une correspondance de jeu de caractères. Par exemple:

(abc)

Correspondra soit a, b, ou c. Cela agit comme un bloc et les crochets ne sont que des structures de contrôle. Vous pouvez également spécifier une plage de caractères:

(a-c)

Ou annulez l'ensemble, qui correspondra à tout caractère qui ne se trouve pas dans l'ensemble:

(^a-c)

Quantificateurs

Les quantificateurs sont une partie importante de Regex. Ils vous permettent de faire correspondre des chaînes dont vous ne connaissez pas le exact format, mais vous avez une assez bonne idée.

le + L'opérateur de l'exemple de courrier électronique est un quantificateur, en particulier le quantificateur «un ou plusieurs». Si nous ne savons pas combien de temps une certaine chaîne est, mais nous savons qu'elle est composée de caractères alphanumériques (et n'est pas vide), nous pouvons écrire:

w+

En plus de +, Il y a aussi:

  • le * , qui correspond à «zéro ou plus». Essentiellement identique à +, sauf qu'il a la possibilité de ne pas trouver de correspondance.
  • le ? , qui correspond à «zéro ou un». Cela a pour effet de rendre un caractère facultatif; soit il est là, soit il ne l'est pas, et il ne correspondra pas plus d'une fois.
  • Quantificateurs numériques. Il peut s'agir d'un seul numéro comme {3}, ce qui signifie «exactement 3 fois» ou une plage comme {3-6}. Vous pouvez laisser de côté le deuxième numéro pour le rendre illimité. Par exemple, {3,} signifie «3 fois ou plus». Curieusement, vous ne pouvez pas omettre le premier numéro, donc si vous voulez "3 fois ou moins", vous devrez

Quantificateurs gourmands et paresseux

Sous le capot, le * et + les opérateurs sont glouton. Il correspond autant que possible et donne ce qui est nécessaire pour démarrer le bloc suivant. Cela peut être un énorme problème.

Voici un exemple: disons que vous essayez de faire correspondre HTML, ou autre chose avec des accolades fermantes. Votre texte d'entrée est:

Hello World

Et vous voulez faire correspondre tout entre les crochets. Vous pouvez écrire quelque chose comme:

<.*>

C'est la bonne idée, mais elle échoue pour une raison cruciale: le moteur Regex correspond "div>Hello World

"Pour la séquence .*, puis revient en arrière jusqu'à ce que le bloc suivant corresponde, dans ce cas, à un crochet fermant (>). Vous vous attendriez à ce qu'il revienne en arrière pour correspondre uniquement à "div», Puis répétez à nouveau pour faire correspondre le div de clôture. Mais le backtracker court à partir de la fin de la chaîne et s'arrêtera sur le crochet de fin, ce qui finira par correspondre à tout ce qui se trouve à l'intérieur des crochets.

La solution consiste à rendre notre quantificateur paresseux, ce qui signifie qu'il correspondra au moins de caractères possible. Sous le capot, cela ne correspondra en fait qu'à un seul personnage, puis s'agrandira pour remplir l'espace jusqu'à la prochaine correspondance de bloc, ce qui le rend beaucoup plus performant dans les grandes opérations Regex.

Rendre un quantificateur paresseux se fait en ajoutant un point d'interrogation directement après le quantificateur. C'est un peu déroutant car ? est déjà un quantificateur (et est en fait gourmand par défaut). Pour notre exemple HTML, le Regex est corrigé avec cet ajout simple:

<.*?>

L'opérateur paresseux peut être cloué sur n'importe quel quantificateur, y compris +?, {0,3}?, et même ??. Bien que le dernier n'ait aucun effet; parce que vous faites correspondre zéro ou un caractère de toute façon, il n'y a pas de place pour développer.

Regroupement et lookarounds

Les groupes dans Regex ont de nombreux objectifs. À un niveau de base, ils réunissent plusieurs jetons en un seul bloc. Par exemple, vous pouvez créer un groupe, puis utiliser un quantificateur sur l'ensemble du groupe:

ba(na)+

Ceci regroupe le «na» répété pour correspondre à la phrase banana, et banananana, etc. Sans le groupe, le moteur Regex correspondrait à plusieurs reprises au caractère de fin.

Ce type de groupe avec deux parenthèses simples est appelé groupe de capture et l'inclura dans la sortie:

Si vous souhaitez éviter cela, et simplement regrouper les jetons pour des raisons d'exécution, vous pouvez utiliser un groupe non capturant:

ba(?:na)

Le point d'interrogation (un caractère réservé) définit un groupe non standard et le caractère suivant définit de quel type de groupe il s'agit. Le démarrage de groupes avec un point d'interrogation est idéal, car sinon, si vous souhaitez faire correspondre des points-virgules dans un groupe, vous devez les échapper sans raison valable. Mais toi toujours doivent échapper aux points d'interrogation dans Regex.

Vous pouvez également nommer vos groupes, pour plus de commodité, lorsque vous travaillez avec la sortie:

(?'group')

Vous pouvez les référencer dans votre Regex, ce qui les rend similaires aux variables. Vous pouvez référencer des groupes non nommés avec le jeton 1, mais cela ne monte que jusqu'à 7, après quoi vous devrez commencer à nommer les groupes. La syntaxe pour référencer des groupes nommés est:

k{group}

Cela fait référence aux résultats du groupe nommé, qui peut être dynamique. Essentiellement, il vérifie si le groupe se produit plusieurs fois mais ne se soucie pas de la position. Par exemple, cela peut être utilisé pour faire correspondre tout le texte entre trois mots identiques:

La classe de groupe est l'endroit où vous trouverez la plupart de la structure de contrôle de Regex, y compris les têtes de recherche. Les Lookaheads s'assurent qu'une expression doit correspondre mais ne l'inclut pas dans le résultat. D'une certaine manière, elle est similaire à une instruction if et ne correspondra pas si elle renvoie false.

La syntaxe d'un lookahead positif est (?=). Voici un exemple:

Cela correspond très proprement à la partie nom d'une adresse e-mail, en arrêtant l'exécution à la division @. Les têtes de recherche ne consomment aucun caractère. Par conséquent, si vous souhaitez continuer à courir après la réussite d'une tête de recherche, vous pouvez toujours faire correspondre le caractère utilisé dans la tête de recherche.

En plus des anticipations positives, il existe également:

  • (?!) – Lookaheads négatifs, qui assurent une expression ne fait pas rencontre.
  • (?<=) - Lookbehinds positifs, qui ne sont pas pris en charge partout en raison de certaines contraintes techniques. Ceux-ci sont placés avant l'expression que vous souhaitez faire correspondre, et ils doivent avoir une largeur fixe (c'est-à-dire, aucun quantificateur sauf {number}. Dans cet exemple, vous pouvez utiliser (?<=@)w+.w+ pour correspondre à la partie domaine de l'e-mail.
  • (? - Lookbehinds négatifs, qui sont les mêmes que les lookbehinds positifs, mais annulés.

Différences entre les moteurs Regex

Tous les Regex ne sont pas créés égaux. La plupart des moteurs Regex ne suivent aucune norme spécifique, et certains modifient un peu les choses en fonction de leur langue. Certaines fonctionnalités qui fonctionnent dans une langue peuvent ne pas fonctionner dans une autre.

Par exemple, les versions de sed compilés pour macOS et FreeBSD ne prennent pas en charge l'utilisation t pour représenter un caractère de tabulation. Vous devez copier manuellement un caractère de tabulation et le coller dans le terminal pour utiliser un onglet en ligne de commande sed.

La plupart de ce tutoriel est compatible avec PCRE, le moteur Regex par défaut utilisé pour PHP. Mais le moteur Regex de JavaScript est différent: il ne prend pas en charge les groupes de capture nommés avec des guillemets (il veut des crochets) et ne peut pas faire de récursivité, entre autres. Même PCRE n'est pas entièrement compatible avec différentes versions et présente de nombreuses différences par rapport à l'expression rationnelle de Perl.

Il existe trop de différences mineures pour les répertorier ici, vous pouvez donc utiliser ce tableau de référence pour comparer les différences entre plusieurs moteurs Regex. De plus, les débogueurs Regex comme Regex101 vous permettent de changer de moteur Regex, alors assurez-vous de déboguer en utilisant le bon moteur.

Comment exécuter Regex

Nous avons discuté de la partie correspondante des expressions régulières, qui constitue l'essentiel de ce qui fait une expression régulière. Mais lorsque vous voulez réellement exécuter votre Regex, vous devez le former en une expression régulière complète.

Cela prend généralement le format:

/match/g

Tout à l'intérieur des barres obliques est notre match. le g est un modificateur de mode. Dans ce cas, il indique au moteur de ne pas s'arrêter après avoir trouvé la première correspondance. Pour rechercher et remplacer Regex, vous devrez souvent le formater comme suit:

/find/replace/g

Cela remplace tout au long du fichier. Vous pouvez utiliser des références de groupe de capture lors du remplacement, ce qui rend Regex très bon pour formater le texte. Par exemple, cette expression régulière correspondra à toutes les balises HTML et remplacera les crochets standard par des crochets:

/<(.+?)>/(1)/g

Lorsque cela fonctionne, le moteur correspondra

et

, vous permettant de remplacer ce texte (et ce texte uniquement). Comme vous pouvez le voir, le code HTML interne n'est pas affecté:

Cela rend Regex très utile pour rechercher et remplacer du texte. L'utilitaire de ligne de commande pour ce faire est sed, qui utilise le format de base de:

sed '/find/replace/g' file > file

Cela s'exécute sur un fichier et sort vers STDOUT. Vous devrez le rediriger vers lui-même (comme illustré ici) pour remplacer réellement le fichier sur le disque.

Regex est également pris en charge dans de nombreux éditeurs de texte et peut vraiment accélérer votre flux de travail lors des opérations par lots. Vim, Atom et VS Code ont tous Regex find and replace intégré.

Bien sûr, Regex peut également être utilisé par programme et est généralement intégré à de nombreuses langues. L'implémentation exacte dépendra de la langue, vous devrez donc consulter la documentation de votre langue.

Par exemple, en JavaScript, l'expression régulière peut être créée littéralement ou dynamiquement à l'aide de l'objet RegExp global:

var re = new RegExp('abc')

Ceci peut être utilisé directement en appelant le .exec() de l'objet regex nouvellement créé, ou en utilisant la méthode .replace(), .match(), et .matchAll() méthodes sur les chaînes.