Comment utilisez-vous réellement Regex?

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

Syntaxe Regex, expliquée

Regex a la réputation d'avoir syntaxe horrible, mais c'est beaucoup plus facile à écrire qu'à lire. Par exemple, voici une expression régulière générale pour un validateur de courrier électronique conforme à la RFC 5322:

(?: [a-z0-9! # $% & amp; '* +/=? ^ _ `{|} ~ -] + (?: . [a-z0-9! # $% & amp;' * +/=? ^ _ `{|} ~ -] +) * |” (?: [ x01- x08 x0b x0c x0e- x1f x21 x23- x5b x5d- x7f] | \ [ x01- x09 x0b x0c x0e- 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 – x08 x0b x0c x0e- x1f x21- x5a x53- x7f] | \ [ x01- x09 x0b x0c x0e- 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 désordre est en fait la programmation d'une machine à états finis. Cette machine fonctionne pour chaque personnage, évolue et correspond en fonction des règles que vous avez définies. De nombreux outils en ligne rendront des diagrammes de chemin de fer, montrant le fonctionnement de votre machine Regex. Voici ce même Regex sous forme visuelle:

Toujours très déroutant, mais c'est beaucoup plus compréhensible. C & # 8217; est une machine avec des pièces mobiles qui ont des règles définissant comment tout cela s & # 39; assemble. Vous pouvez voir comment quelqu'un a assemblé ceci; ce n'est pas seulement un gros glob de texte.

Tout d'abord: utilisez un débogueur Regex

Avant de commencer, à moins que votre expression régulière ne soit particulièrement courte ou que vous & # 8217; êtes particulièrement compétent, vous devriez utilisez un débogueur en ligne lors de l'écriture et du test. Cela rend la compréhension de la syntaxe beaucoup plus facile. 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 expression régulière de correspondance de courrier électronique 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 à 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 n ° 1 capture le nom de l'adresse e-mail et tout ce qui suit correspond au domaine.

Le Regex qui définit le groupe n ° 1 dans notre exemple d'e-mail 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, tout le courrier électronique). Mais il renvoie également chaque groupe de capture, ce qui rend ce Regex utile pour extraire des noms d'e-mails.

Le point est le symbole de & # 8220; Tout caractère sauf Newline. & # 8221; Cela correspond à tout sur une ligne, donc si vous transmettez cette adresse e-mail Regex, une adresse comme:

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

Cela correspondrait à% $ # ^ & amp ;% * #% $ # ^ Comme nom, même si c'est ridicule.

Le symbole plus (+) est une structure de contrôle qui signifie & # 8220; faire correspondre le caractère ou le groupe précédent une ou plusieurs fois. & # 8221; Cela garantit que tout le nom 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 lorsqu'il atteint le symbole @. Le groupe suivant commence alors, qui correspond à nouveau à plusieurs caractères jusqu'à ce qu'il atteigne un caractère point.

Étant donné que des caractères tels que des points, des parenthèses et des barres obliques sont utilisés dans le cadre de la syntaxe de 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, nous écrivons . et l'analyseur le traite comme un symbole signifiant & # 8220; correspond à un point. & # 8221;

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, l'expression régulière:

he + llo

correspondra au mot & # 8220; bonjour & # 8221; avec n'importe quel nombre de e & # 8217; s. Tous les autres caractères 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. Celles-ci peuvent varier en fonction de l'implémentation Regex, mais celles-ci sont standard:

  • . & # 8211; correspond à tout sauf à une nouvelle ligne.
  • w & # 8211; correspond à n'importe quel mot & # 8220; & # 8221; caractère, y compris les chiffres et les traits de soulignement.
  • d & # 8211; correspond aux nombres.
  • b & # 8211; correspond aux caractères d'espacement (c'est-à-dire espace, tabulation, nouvelle ligne).

Ces trois éléments ont tous des équivalents 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 à 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:

[ac]

Ou annuler l'ensemble, qui correspondra à tout caractère qui n'est pas & # 8217; t dans l'ensemble:

[^ ac]

Quantificateurs

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

L'opérateur + de l'exemple d'e-mail est un quantificateur, en particulier celui de & # 8220; ou plus & # 8221; quantificateur. Si nous ne savons pas la longueur d'une certaine chaîne, mais que 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:

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

Quantificateurs gourmands et paresseux

Sous le capot, le Les opérateurs * et + sont gourmands. Il correspond autant que possible et rend 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 le HTML, ou toute autre chose avec des accolades fermantes. Votre texte d'entrée est:

& lt; div & gt; Hello World & lt;/div & gt;

Et vous voulez faire correspondre tout ce qui est entre parenthèses. Vous pouvez écrire quelque chose comme:

& lt;. * & Gt;

C'est la bonne idée, mais elle échoue pour une raison cruciale: le moteur Regex correspond à & # 8220; div & gt; Hello World & lt;/div & gt; & # 8221; pour la séquence. *, puis revient en arrière jusqu'à ce que le bloc suivant corresponde, dans ce cas, à un crochet fermant (& gt;). Vous vous attendez à ce qu'il revienne en arrière pour ne correspondre qu'à & # 8220; div & # 8220 ;, puis se répète à nouveau pour correspondre au div de clôture. Mais le backtracker part de la fin de la chaîne et s'arrêtera sur le crochet de fin, ce qui finit par correspondre à tout ce qui se trouve entre les crochets.

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

Faire un quantificateur paresseux se fait en ajoutant un point d'interrogation directement après le quantificateur. C'est un peu déroutant parce que? est déjà un quantificateur (et est en fait gourmand par défaut). Pour notre exemple HTML, l'expression régulière est corrigée avec cet ajout simple:

& lt;. *? & Gt;

L'opérateur paresseux peut être attaché à 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 se développer.

Groupement et Lookarounds

< p>Les groupes dans Regex ont de nombreux objectifs. Au 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) +

Cela regroupe les répétitions & # 8220; na & # 8221; pour correspondre à l'expression banane, et bananane, et ainsi de suite. Sans le groupe, le moteur Regex ferait simplement correspondre le caractère de fin encore et encore.

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

Si vous souhaitez éviter cela et regroupez simplement les jetons pour des raisons d'exécution, vous peut 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. L'idéal est de commencer les groupes avec un point d'interrogation, car sinon, si vous vouliez faire correspondre des points-virgules dans un groupe, vous auriez besoin de les échapper sans raison valable. Mais vous devez toujours éviter les points d'interrogation dans Regex.

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

(? 'Group')

Vous pouvez référencer ceux-ci dans votre Regex, ce qui les fait fonctionner comme des variables. Vous pouvez référencer des groupes non nommés avec le jeton 1, mais cela ne va que jusqu'à 7, après quoi vous devrez commencer à nommer les groupes. La syntaxe pour référencer les 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:

< p>La classe de groupe est l'endroit où vous trouverez la plupart de la structure de contrôle de Regex, y compris les anticipations. 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 pour une anticipation positive est (? =). Voici un exemple:

Cela correspond très clairement à la partie nom d'une adresse e-mail, en arrêtant l'exécution à la division @. Les Lookaheads ne consomment aucun caractère, donc si vous voulez continuer à courir après une recherche réussie, vous pouvez toujours faire correspondre le caractère utilisé dans Lookahead.

En plus des lookaheads positifs, il y a aussi:

  • (?!) & # 8211; Lookaheads négatifs, qui garantissent qu'une expression ne correspond pas.
  • (? & lt; =) & # 8211; Des regards en arrière positifs, qui ne sont pas pris en charge partout en raison de certaines contraintes techniques. Celles-ci sont placées avant l'expression que vous voulez faire correspondre et elles doivent avoir une largeur fixe (c'est-à-dire pas de quantificateurs sauf {nombre}. Dans cet exemple, vous pouvez utiliser (? & Lt; = @) w + . W + pour correspondre la partie domaine de l'e-mail.
  • (? & lt ;!) & # 8211; Lookbehinds négatifs, qui sont identiques aux 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 changent un peu les choses en conséquence leur langue. Certaines fonctionnalités qui fonctionnent dans une langue peuvent ne pas fonctionner dans une autre.

Par exemple, les versions de sed compilées pour macOS et FreeBSD ne prennent pas en charge l'utilisation de 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 entre 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 il présente de nombreuses différences par rapport à Perl regex.

Il y a trop de différences mineures à énumérer 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 la transformer 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 dit au moteur de ne pas s'arrêter de fonctionner 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 dans le fichier. Vous pouvez utiliser des références de groupe de capture lors du remplacement, ce qui rend Regex très bon pour la mise en forme du 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 s'exécute, le moteur correspond à & lt; div & gt; et & lt;/div & gt ;, vous permettant de remplacer ce texte (et ce texte uniquement). Comme vous pouvez le voir, le 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 & gt; file

Ceci s'exécute sur un fichier et sort vers STDOUT. Vous devrez le diriger vers lui-même (comme indiqué 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 lorsque vous effectuez un traitement par lots opérations. Vim, Atom et VS Code ont tous Regex find and replace intégrés.

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

Par exemple, en JavaScript, les regex peuvent être créées littéralement ou dynamiquement en utilisant le global RegExp object:

var re = new RegExp ('abc')

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


Posted

in

by

Tags: