URL: https://linuxfr.org/news/travailler-avec-des-expressions-rationnelles Title: Travailler avec des expressions rationnelles Authors: Denis Dordoigne Davy Defaud, Benoît Sibaud, Nÿco, palm123, Lucas, Michaël, esdeem, Stéphane Aulery, Jiel, Kwiknclean, Xavier Teyssier, ranDom, Nicolas Casanova, Ontologia, Trollgouin, BAud, anaseto et Jiehong Date: 2015年03月25日T13:02:24+01:00 License: CC By-SA Tags: regex, grep, sed, vi et emacs Score: 85 Les expressions rationnelles sont un outil d’analyse de texte par un ordinateur. Elles permettent de décrire des enchaînements de caractères d’une complexité suffisamment grande pour être réellement utiles, mais suffisamment faible pour être implémentées efficacement. Elles sont d’une importance capitale pour le théoricien des langages comme pour l’*UNIX power user*. Dans cette dépêche, nous : - décrivons brièvement la notion abstraite d’expression rationnelle et recensons les implémentations les plus courantes sur un système Unix ; - présentons quelques commandes permettant de rechercher des motifs décrits par une expression rationnelle dans un texte, réécrire des fichiers automatiquement ou transformer et analyser des fichiers *structurés* automatiquement en utilisant des expressions rationnelles ; - montrons comment améliorer votre productivité avec Emacs grâce aux expressions rationnelles. Dans cette dépêche, nous allons nous pencher sur les expressions rationnelles (souvent nommées abusivement _expressions régulières_ suite à une traduction littérale de _regular expression_). Elles permettent de représenter formellement un motif de recherche, par exemple : un caractère alphabétique majuscule suivi de quatre caractères minuscules, puis deux chiffres et un point à la fin. Les expressions rationnelles représentent un outil puissant pour qui sait les utiliser à bon escient mais nécessitent une phase d’apprentissage non négligeable. La diversité des moteurs et des syntaxes n’aide pas non plus à leur simplicité, et les confusions entre les différents outils peuvent parfois donner des résultats surprenants. ---- ---- ![Expressions régulières](http://xkcd.lapin.org/strips/xkcd-208-expressions-regulieres.gif) _Source : original en VO [XKCD](https://xkcd.com/208/), traduction en [VF](http://xkcd.lapin.org/index.php?number=208)_ Description abstraite et implémentations principales ======================================= Les expressions rationnelles sont souvent utilisées comme brique de l’analyse des textes, pour faire de l’analyse lexicale. Elles sont issues des théories mathématiques des langages formels. Le concept ayant montré sa pertinence, il faut faire face à une richesse des implémentations : [[POSIX]], puis chaque Unix à sa version, GNU, FreeBSD, puis Perl et Emacs, pour les plus répandues. Certaines apportent des extensions (sucre syntaxique +, répétitions, groupes, et _back tracking_). [Wikipédia](https://en.wikipedia.org/wiki/Regular_expression#Examples) fournit divers exemples illustratifs. En voici quelques exemples variés : - recherche de motif avec `grep` pour avoir un filtre pour sélectionner des lignes, pour identifier des fichiers, pour sélectionner des journaux système à une certaine date ou pour rechercher dans les pages de manuel, etc. ; - avec `sed`, transformation de journaux système en format Apache en format tabulaire, transformation de la sortie de Docker, _ps_, etc. ; - dans `Emacs`, mettre en valeur un motif dans du code pour une revue ou pour l’édition, extraire des listes d’un fichier avec _re-search_, etc. Les expressions rationnelles POSIX basiques =========== Les expressions rationelles [[POSIX]] génèrent des machines à état fini déterministe. Elles ne sont ainsi pas capables de faire des retours en arrière. La commande `grep` ----------- Le premier usage des expressions rationnelles pour les utilisateurs de systèmes basés sur GNU/Linux ou Unix est en général la commande `grep`, qui permet de trouver toutes les lignes correspondant à une expression rationnelle. La syntaxe de la commande `grep` est simplement : grep Pour les exemples ci‐dessous, nous ferons des recherches dans le fichier _french_ d’une [[Debian]] stable (paquet [_wfrench_](https://packages.debian.org/search?keywords=wfrench) qui amène le fichier `/usr/share/dict/french`, [[_informations de licence_](http://metadata.ftp-master.debian.org/changelogs/main/w/wfrench/wfrench_1.2.3-10_copyright)]), ce fichier contenant la liste des mots de la langue française à raison d’un mot par ligne. Dans une expression rationnelle, la première règle est que chaque caractère se représente lui‐même, par exemple l’expression rationnelle « `rationnelle` » correspond à « toute ligne contenant un _r_, suivi d’un _a_, suivi d’un _t_, suivi d’un _i_, suivi d’un _o_, suivi d’un _n_, suivi d’un autre _n_, suivi d’un _e_, suivi d’un _l_, suivi d’un autre _l_, suivi d’un _e_, suivi d’un _s_ » : ![$ grep 'rationnelles' french irrationnelles opérationnelles rationnelles](http://denis.dordoigne.eu/dlfp/regex/BRE1.png) Chaque caractère ne représente pas vraiment lui‐même, il existe des exceptions avec des méta‐caractères qui décrivent autre chose qu’eux‐mêmes. Un des plus utilisés de ces méta‐caractères est le point, qui signifie « un caractère quelconque », par exemple l’expression rationnelle « `rationnelle.` » correspond à « toute ligne contenant un _r_, suivi d’un _a_, suivi d’un _t_, suivi d’un _i_, suivi d’un _o_, suivi d’un _n_, suivi d’un autre _n_, suivi d’un _e_, suivi d’un _l_, suivi d’un autre _l_, suivi d’un _e_, suivi d’un caractère quelconque » : ![$ grep 'rationnelle.' french irrationnelles opérationnelles irrationnellement rationnelles](http://denis.dordoigne.eu/dlfp/regex/bre2.png) Le problème des méta‐caractères est qu’on peut vouloir chercher du texte les contenant. Par exemple, dans notre dictionnaire, il y a des abréviations se terminant par un point. Pour qu’un méta‐caractère ne soit pas interprété, il faut le précéder d’une contre‐oblique « \ », par exemple « \\. » représente le caractère point. On peut alors s’amuser à chercher les abréviations d’au moins six caractères, en les décrivant comme « un caractère quelconque, suivi d’un autre caractère quelconque, suivi d’un troisième caractère quelconque, suivi d’un quatrième caractère quelconque, suivi d’un cinquième caractère quelconque, suivi d’un sixième caractère quelconque, suivi d’un point » : ![$ grep '......\.' french arrond. c.‐à‐d.](http://denis.dordoigne.eu/dlfp/regex/BRE3.png) On remarquera que le point lui‐même est un caractère quelconque. Un autre méta‐caractère utile est le crochet, qui permet de décrire un caractère pouvant correspondre à plusieurs valeurs, par exemple une voyelle non accentuée peut être représentée par « `[aeiouy]` » (qu’on peut lire comme « n’importe quel caractère étant soit un _a_, soit un _e_, soit un _i_, soit un _u_, soit un _y_ »). Par exemple, si vous voulez briller en société en citant des mots comportant six voyelles non accentuées à la suite : ![$ grep '[aeiouy][aeiouy][aeiouy][aeiouy][aeiouy][aeiouy]' french rougeoyaient youyou youyous](http://denis.dordoigne.eu/dlfp/regex/BRE4.png) Deux méta‐caractères particuliers sont utiles entre crochets : * le tiret situé entre deux caractères permet de définir une liste de caractères qui se suivent, par exemple « `[a-f]` » définit « soit un _a_, soit un _b_, soit un _c_, soit un _d_, soit un _e_, soit un _f_ » ; * l’accent circonflexe situé au début permet de définir une exclusion de caractères, par exemple « `[\^aeiouy]` » définit « un quelconque caractère qui ne soit ni un _a_, ni un _e_, ni un _i_, ni un _o_, ni un _u_, ni un _y_ »). Ces deux méta‐caractères sont cumulables, par exemple « `[\^a-z]` » définit « un quelconque caractère qui ne soit pas une lettre minuscule non accentuée », ce qui peut nous permettre de trouver tous les mots qui ont à la suite deux caractères qui ne sont pas des lettres : ![$ grep '[^a-z][^a-z]' french c.‐à‐d. ch.-l.](http://denis.dordoigne.eu/dlfp/regex/BRE5.png) On peut économiser les copier‐coller lorsque l’on veut chercher plusieurs fois la même information, en utilisant le symbole « `\{min,max\}` » qui permet d'indiquer que l’on cherche la présence d’un caractère successivement entre _min_ et _max_ fois, par exemple si vous cherchez les mots contenant deux _q_ séparés par 5 à 7 lettres [^1] : ![$ grep 'q[a-z]\{5,7\}q' french quantique quantiques quelconque quelconques quiconque quiproquo quiproquos squelettique squelettiques](http://denis.dordoigne.eu/dlfp/regex/BRE6.png) Il est possible avec certaines versions de _grep_ de spécifier un seul chiffre entre accolades : * si l’on cherche exactement _x_ occurrences, on indique : « `\{x\}` » ; * si l’on cherche de 0 à _x_ occurrences, on indique : « `\{,x\}` » ; * si l’on cherche au moins _x_ occurrences, on indique : « `\{x,\}` ». Ainsi, on pourrait donc abréger la recherche des mots contenant 6 voyelles non accentuées ainsi : ![$ grep '[aeiouy]\{6\}' french rougeoyaient youyou youyous](http://denis.dordoigne.eu/dlfp/regex/BRE7.png) Si l’on veut répéter plusieurs caractères au lieu d’un seul, il faut encadrer la recherche avec des « `\( \)` ». Par exemple, si vous bloquez dans une grille de mots croisés sur la définition « mot contenant sept fois à la suite une consonne suivie d’une voyelle » : ![$ grep '\([^aeiouy][aeiouy]\)\{7\}' french remilitarisation ](http://denis.dordoigne.eu/dlfp/regex/BRE9.png) Le contenu trouvé à partir d’une expression entre parenthèses est dit « capturé », cela signifie qu’il est gardé en mémoire et peut être réutilisé dans l’expression rationnelle. La contenu capturé est accessible en utilisant « `1円` », « `2円` », « `3円` », etc. (en général, on ne peut pas dépasser `9円`). Le numéro de capture est défini en comptant le nombre de parenthèses ouvrantes précédant l’expression capturée. Cela permet par exemple de lister les mots contenant un palindrome de quatre lettres : ![$ grep '\(.\)\(.\)\(.\)\(.\)4円3円2円1円' french caressera caresserai caresseraient caresserais caresserait caresseras paressera paresserai paresseraient paresserais paresserait paresseras querellerez](http://denis.dordoigne.eu/dlfp/regex/BRE10.png) On peut encore affiner les recherches en utilisant les ancres, qui permettent de situer où se situe une expression rationnelle dans la ligne : * le dollar, lorsqu’il est situé à la fin de l’expression rationnelle, représente la fin de la ligne ; * l’accent circonflexe, lorsqu’il est situé au début de l’expression rationnelle, représente le début de la ligne. On peut cumuler les deux ancres dans la même expression, par exemple si l’on veut chercher les vrais palindromes de quatre lettres : ![$ grep '^\(.\)\(.\)2円1円$' french alla elle erre esse](http://denis.dordoigne.eu/dlfp/regex/BRE11.png) Pour en terminer avec les expressions rationnelles POSIX basiques, il ne reste plus qu’un méta‐caractère à présenter, qui est l’astérisque. Ce caractère est équivalent à « `\{0,\}` » : ![$ grep '^d.*ouilles$' french débrouilles dépouilles douilles](http://denis.dordoigne.eu/dlfp/regex/BRE12.png) Utiliser dans vi ----------- [VimRegex](http://vimregex.com) détaille largement le sujet. Extension des expressions rationnelles =========== Les extensions rationnelles basiques étant peu lisibles, la norme POSIX a évolué pour intégrer les expressions rationnelles étendues, aussi appelées « ERE ». grep est mieux avec « -E » ----------- Les versions récentes de `grep` permettent d’utiliser les expressions rationnelles étendues avec l’option `-E`. Si vous ajoutez l’option `-E` à `grep`, vous devez modifier votre expression rationnelle ainsi : * `\{` et `\}` deviennent `{` et `}` ; * `\(` et `\)` deviennent `(` et `)` ; * tous les autres méta‐caractères (« `.` », « `[` », « `]` », « `-` », « `^` », « `$` », « `*` », « `1円` », etc.) sont inchangés. Outre cette suppression des contre‐obliques superflus, les expressions rationnelles étendues apportent trois nouveaux méta‐caractères. Le premier est « `?` » qui est un synonyme de « `{0,1}` », qui permet par exemple de chercher les palindromes de 4 ou 6 lettres avec une seule expression : ![ $ grep -E '^(.)(.)((.)4円)?2円1円$' french alla elle erre esse selles serres](http://denis.dordoigne.eu/dlfp/regex/ERE1.png) On dispose aussi de « `+` » qui est un synonyme de « `{1,}` » : ![$ grep -E '^cré+e$' french crée créée ](http://denis.dordoigne.eu/dlfp/regex/ERE2.png) Enfin, le dernier méta‐caractère spécifique aux expressions rationnelles étendues est le « `|` » qui permet de séparer plusieurs options : ![$ grep -E '^(gr|f|citr)ouille$' french citrouille fouille grouille](http://denis.dordoigne.eu/dlfp/regex/ERE3.png) Les classes de caractères ----------- POSIX prévoit des classes de caractère, qui sont des notations spécifiques entre crochets. À noter que les classes de caractères sont aussi bien gérées par les expressions rationnelles basiques qu’étendues (il n’y a donc pas besoin d’utiliser l’option `-E` pour en bénéficier), mais il existe des implémentations d’expressions rationnelles basiques non compatibles POSIX qui ne les acceptent pas. Les classes de caractères sont des mots ou abréviations en anglais désignant ce à quoi ils correspondent et encadrés par « `[:` » et « `:]` » : * `[:digit:]` : désigne un chiffre décimal (équivalent à `[0-9]`) ; * `[:lower:]` : désigne une lettre minuscule (équivalent à `[a-z]`) ; * `[:upper:]` : désigne une lettre majuscule (équivalent à `[A-Z]`) ; * `[:alpha:]` : désigne une lettre minuscule ou majuscule (équivalent à `[A-Za-z]`) ; * `[:alnum:]` : désigne une lettre minuscule ou majuscule ou un chiffre (équivalent à `[A-Za-z0-9]`) ; * `[:xdigit:]` : désigne un chiffre hexadécimal (équivalent à [0-9a-fA-F]) ; * `[:space:]` : désigne un caractère d’espacement (espace, tabulation, retour chariot, etc.) ; * `[:blank:]` : désigne un espace ou une tabulation horizontale (à ne pas confondre avec `[:space:]`) ; * `[:punct:]` : désigne à un crochet ou un caractère de la classe suivante : `['!"#$%&()*+,./:;<=>?@\^_`{|}~-]` ; * `[:cntrl:]` : désigne un [[caractère de contrôle]] ; * `[:print:]` : désigne un caractère affichable (ainsi qu’une espace), cette classe est à peu près le contraire de `[:cntrl:]` ; * `[:graph:]` : désigne l’ensemble des caractères visibles, sauf les espaces, les caractères de contrôle, etc. (équivalent à `[\x21-\x7E]`). Pour aller plus loin =========== Attention au GLOB ----------- Dans les exemples précédents, il était important d’utiliser de simples apostrophes pour éviter l’interprétation de caractères spéciaux par le _Shell_. Outils pour tester vos expressions rationnelles ----------- Plusieurs outils s’offrent à vous pour tester et triturer dans tous les sens vos expressions rationnelles, comme par exemple le site [_Regex Pal_](http://regexpal.com/), qui propose notamment de la coloration syntaxique et se veut « temps réel » dans les modifications, ou [_regex101_](https://regex101.com) qui permet de tester des expressions rationnelles Python, JavaScript ou [PCRE](https://fr.wikipedia.org/wiki/PCRE "Perl Compatible Regular Expression — Expressions rationnelles compatibles Perl"). Ne pas toujours utiliser les expressions rationnelles ----------- Les expressions rationnelles ne sont par exemple pas l’outil idéal pour analyser du XML ou du HTML. Jouer avec les expressions rationnelles ----------- Voir la dépêche [_Regexcrossword : un subtil mélange de sudoku et de mots croisés, à la sauce Regex_](//linuxfr.org/news/regexcrossword-un-subtil-melange-de-sudoku-et-de-mots-croises-a-la-sauce-regex), ainsi que [la chasse au trésor du MIT en 2014](http://www.i-programmer.info/news/144-graphics-and-games/5450-can-you-do-the-regular-expression-crossword.html), etc. Un peu de théorie ----------- ### Les automates finis La base théorique des expressions rationnelles se trouve dans la théorie des langages. Elles permettent notamment de décrire les [langages rationnels](https://fr.wikipedia.org/wiki/Langage_rationnel). Elles sont fortement liées aux [automates finis](https://fr.wikipedia.org/wiki/Automate_fini). Pour illustrer le parallèle nous allons utiliser les caractères et les quantificateurs de base : - `a` qui permet de reconnaître la lettre `a` ; - `?` qui permet de définir un groupe optionnel ; - `*` qui permet de définir un groupe se répétant zéro fois ou plus ; - `+` qui permet de définir un groupe se répétant une fois ou plus. ## Littérature - une des ressources en ligne indispensables sur les moteurs d’expressions rationnelles se trouve sur le site de Russ Cox : ; - un vieil [article de Mark‐Jason Dominus](http://perl.plover.com/Regex/article.html) explique le fonctionnement des expressions rationnelles et leur application dans Perl ; - une implémentation expliquée d’un moteur d’expressions rationnelles est disponible dans un [cours de l’Université de Vancouver](http://ezekiel.vancouver.wsu.edu/~cs317/archive/projects/grep/grep.pdf). _[^(1)] Avec ça vous allez vraiment briller en société, il faudra juste trouver un moyen d’intégrer ça dans la conversation._

AltStyle によって変換されたページ (->オリジナル) /