Pourquoi mon expression régulière fonctionne-t-elle dans X mais pas dans Y?


77

J'ai écrit une expression régulière qui fonctionne bien dans un certain programme (grep, sed, awk, perl, python, ruby, ksh, bash, zsh, find, emacs, vi, vim, gedit,…). Mais lorsque je l'utilise dans un programme différent (ou sur une variante unix différente), il ne correspond plus. Pourquoi?

Réponses:


103

Malheureusement, pour des raisons historiques, différents outils ont une syntaxe d'expression régulière légèrement différente, et parfois certaines implémentations ont des extensions qui ne sont pas prises en charge par d'autres outils. Bien qu'il existe un terrain d'entente, il semble que chaque rédacteur d'outils ait fait des choix différents.

La conséquence est que si vous avez une expression régulière qui fonctionne dans un outil, vous devrez peut-être la modifier pour qu'elle fonctionne dans un autre outil. Les principales différences entre les outils courants sont les suivantes:

  • si les opérateurs +?|(){}exigent une barre oblique inverse;
  • quelles extensions sont supportées au-delà des bases .[]*^$et généralement+?|()

Dans cette réponse, je liste les principales normes . Consultez la documentation des outils que vous utilisez pour plus de détails.

La comparaison des moteurs d'expression régulière de Wikipedia comporte un tableau répertoriant les fonctionnalités prises en charge par les implémentations courantes.

Expressions régulières de base (BRE)

Les expressions régulières de base sont codifiées par le standard POSIX . C'est la syntaxe utilisée par grep, sedet vi. Cette syntaxe fournit les fonctionnalités suivantes:

  • ^et $ne correspondent qu'au début et à la fin d'une ligne.
  • . correspond à n'importe quel caractère (ou à tout caractère sauf une nouvelle ligne).
  • […]correspond à n'importe quel caractère indiqué entre crochets (jeu de caractères). Si le premier caractère après le crochet d’ouverture est un ^, les caractères qui ne sont pas répertoriés sont mis en correspondance. Pour inclure un ], mettez-le immédiatement après l'ouverture [(ou après [^s'il s'agit d'un ensemble négatif). Si -est compris entre deux caractères, il désigne une plage; pour inclure un littéral -, mettez-le là où il ne peut pas être analysé comme une plage.
  • La barre oblique inverse avant tout ^$.*\[cite le caractère suivant.
  • * correspond au caractère précédent ou à la sous-expression 0, 1 fois ou plus.
  • \(…\)est un groupe syntaxique à utiliser avec l' *opérateur, les références arrières et les \DIGITremplacements.
  • Les références arrières \1, \2... correspondent au texte exact abondés par le Groupe correspondant, par exemple les \(fo*\)\(ba*\)\1matchs foobaafoomais non foobaafo. Il n'y a pas de moyen standard de se référer au 10ème groupe et au-delà (la signification standard de \10est le premier groupe suivi de a 0).

Les fonctionnalités suivantes sont également standard, mais sont absentes de certaines implémentations restreintes:

  • \{m,n\}correspond au caractère ou à la sous-expression précédent entre m et n fois; n ou m peuvent être omis et signifie exactement m .\{m\}
  • Entre parenthèses, des classes de caractères peuvent être utilisées, par exemple, elles [[:alpha:]]correspondent à n'importe quelle lettre. Les implémentations modernes des expressions entre crochets ) incluent également des éléments de classement tels que [.ll.]et des classes d'équivalence telles que [=a=].

Ce qui suit sont des extensions communes (en particulier dans les outils GNU), mais ils ne sont pas présents dans toutes les implémentations. Consultez le manuel de l'outil que vous utilisez.

  • \|pour alternance: foo\|barallumettes fooou bar.
  • \?(abréviation de \{0,1\}) et \+(abréviation de \{1,\}) correspondent au caractère ou à la sous-expression précédent au plus une fois, ou au moins une fois, respectivement.
  • \ncorrespond à une nouvelle ligne, \tcorrespond à un onglet, etc.
  • \wcorrespond à n’importe quel constituant du mot (abréviation de [_[:alnum:]]mais avec des variations en ce qui concerne la localisation) et \Wcorrespond à tout caractère qui n’est pas un constituant du mot.
  • \<et ne \>correspond à la chaîne vide qu'au début ou à la fin d'un mot, respectivement; \bcorrespond non plus et \Bcorrespond où \bnon.

Notez que les outils sans \|opérateur n'ont pas toute la puissance des expressions régulières. Les références arrières permettent quelques opérations supplémentaires qui ne peuvent pas être réalisées avec des expressions régulières au sens mathématique.

Expressions régulières étendues (ERE)

Les expressions régulières étendues sont codifiées par le standard POSIX . Leur principal avantage sur BRE est la régularité: tous les opérateurs standard sont des caractères de ponctuation nus, une barre oblique inverse avant qu'un caractère de ponctuation ne le cite toujours. C'est la syntaxe utilisée par awk, grep -Eou egrep, GNU sed -ret l'=~ opérateur bash . Cette syntaxe fournit les fonctionnalités suivantes:

  • ^et $ne correspondent qu'au début et à la fin d'une ligne.
  • . correspond à n'importe quel caractère (ou à tout caractère sauf une nouvelle ligne).
  • […]correspond à n'importe quel caractère indiqué entre crochets (jeu de caractères). La complémentation avec une initiale ^et des plages fonctionne comme dans le BRE (voir ci-dessus). Les classes de caractères peuvent être utilisées mais sont absentes de quelques implémentations. Les implémentations modernes supportent également les classes d'équivalence et les éléments de classement. Une barre oblique inversée entre crochets cite le caractère suivant dans certaines implémentations, mais pas toutes; utiliser \\pour signifier une barre oblique inverse pour la portabilité.
  • (…)est un groupe syntaxique, à utiliser avec *ou en \DIGITremplacement.
  • |pour alternance: foo|barallumettes fooou bar.
  • *, +et ?correspond au nombre de fois précédent le caractère ou la sous-expression: 0 ou plus pour *, 1 ou plus pour +, 0 ou 1 pour ?.
  • La barre oblique inverse cite le caractère suivant s'il n'est pas alphanumérique.
  • {m,n}fait correspondre le caractère ou la sous-expression précédent entre m et n fois (manquant dans certaines implémentations); n ou m peuvent être omis et signifie exactement m .{m}
  • Quelques extensions courantes comme dans le BRE: les références arrières (notamment absentes dans awk sauf dans l'implémentation busybox où vous pouvez utiliser ); caractères spéciaux , etc .; limites de mots et , constituants de mots et ,…\DIGIT$0 ~ "(...)\\1"\n\t\b\B\b\B

PCRE (expressions régulières compatibles Perl)

PCRE sont des extensions d'ERE, introduites à l'origine par Perl et adoptées par GNU, grep -Painsi que de nombreux outils et langages de programmation modernes , généralement via la bibliothèque PCRE . Voir la documentation Perl pour une bonne mise en forme avec des exemples. Toutes les fonctionnalités de la dernière version de Perl ne sont pas prises en charge par PCRE (par exemple, l’exécution de code Perl n’est prise en charge que par Perl). Voir le manuel PCRE pour un résumé des fonctionnalités prises en charge. Les principaux ajouts à ERE sont:

  • (?:…)est un groupe non capturant: comme (…), mais ne compte pas pour les références arrières.
  • (?=FOO)BAR(lookahead) correspond BAR, mais uniquement s'il existe une correspondance pour un FOOdépart à la même position. Ceci est très utile pour ancrer une correspondance sans inclure le texte suivant dans la correspondance: foo(?=bar)correspond foomais uniquement s'il est suivi de bar.
  • (?!FOO)BAR(lookahead négatif) correspond BAR, mais il n'y a pas aussi de correspondance pour FOOla même position. Par exemple, (?!foo)[a-z]+correspond à tout mot minuscule qui ne commence pas par foo; [a-z]+(?![0-9)correspond à n'importe quel mot en minuscule qui n'est pas suivi d'un chiffre (donc dans foo123, il correspond fomais pas foo).
  • (?<=FOO)BAR(lookbehind) correspond BAR, mais uniquement si elle est immédiatement précédée d'une correspondance pour FOO. FOOdoit avoir une longueur connue (vous ne pouvez pas utiliser d'opérateurs de répétition tels que *). Cela est très utile pour ancrer une correspondance sans inclure le texte précédent dans la correspondance: (?<=^| )foocorrespond, foomais uniquement si elle est précédée d'un espace ou du début de la chaîne.
  • (?<!FOO)BAR(lookbehind négatif) correspond BAR, mais seulement si elle n'est pas immédiatement précédée d'une correspondance pour FOO. FOOdoit avoir une longueur connue (vous ne pouvez pas utiliser d'opérateurs de répétition tels que *). Cela est très utile pour ancrer une correspondance sans inclure le texte précédent dans la correspondance: (?<![a-z])foocorrespond, foomais uniquement si elle n'est pas précédée d'une lettre minuscule.

Emacs

La syntaxe d'Emacs est intermédiaire entre BRE et ERE. En plus d’Emacs, c’est la syntaxe par défaut de -regexGNU find. Emacs propose les opérateurs suivants:

  • ^, $, ., […], *, +, ?Comme dans ERE
  • \(…\), \|, \{…\}, Comme dans BRE\DIGIT
  • plus de séquences de lettres antislash ; \<et \>pour les limites de mots; et plus encore dans les versions récentes d’Emacs, qui ne sont souvent pas prises en charge par les autres moteurs utilisant une syntaxe semblable à celle d’Emacs.

Globes de coquille

Les globs de shell (caractères génériques) effectuent une correspondance de modèle avec une syntaxe complètement différente de celle des expressions régulières et moins puissante. En plus des shells, ces caractères génériques sont disponibles avec d'autres outils tels que les find -namefiltres et les filtres rsync. Les modèles POSIX incluent les fonctionnalités suivantes:

  • ? correspond à n'importe quel caractère unique.
  • […]est un jeu de caractères comme dans les syntaxes d'expression régulière communes. Certains coquillages ne supportent pas les classes de caractères. Certains coquillages exigent !au lieu d’ ^annuler l’ensemble.
  • *correspond à n'importe quelle séquence de caractères (souvent sauf /lorsque les chemins de fichiers correspondent; s'il /est exclu *, **inclut parfois /, mais vérifiez la documentation de l'outil).
  • La barre oblique inverse cite le caractère suivant.

Ksh offre des fonctionnalités supplémentaires qui donnent à son modèle la pleine puissance des expressions régulières. Ces fonctionnalités sont également disponibles dans bash après l'exécution shopt -s extglob. Zsh a une syntaxe différente mais peut également supporter la syntaxe de ksh après setopt ksh_glob.


Les autres RE riches que vous pouvez mentionner sont celles de vim's et d'AT & T libast (comme dans ksh93).
Stéphane Chazelas

@ StéphaneChazelas À part vim, quel programme utilise v regexps? En dehors de ksh, quel programme utilise libast?
Gilles, arrête d'être méchant

tous l'ensemble AT & T outil utilise le SRE AT & T ( grep, tw, expr...). À part ksh, cet ensemble d'outils est rarement trouvé en dehors d'AT & T.
Stéphane Chazelas

D'après ma compréhension (et celle de Wikipedia), votre terme "classe de caractères" fait en réalité référence à "classe de caractères POSIX" ... mais il regex(7)est d'accord avec vous et appelle [these]"expressions entre crochets" et (à l'intérieur de "expressions entre crochets") [:these:]"classes de caractères". Je ne sais pas comment répondre au mieux à cette question.
Adam Katz

Peu importe comment vous les appelez, ils supportent les gammes. Il convient de noter que -cette option spécifie une plage et doit être soit échappée, en premier (après l'option ^), soit la dernière si elle doit être prise à la lettre. (J'ai vu beaucoup de bugs provenant, par exemple, de la [A-z]remarque de changement de casse), qui correspond aux caractères des codes 65 à 122 et inclut accidentellement chacun des éléments suivants:. [\]^_`J'ai également vu qu'il était valide mais déroutant de [!-~]faire correspondre tous les caractères imprimables dans ANSI , que je préfère voir [\x21-\x7e], qui est au moins simple dans son action bien que déroutant dans une dimension différente.)
Adam Katz
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.