perlrequick - Les expressions rationnelles Perl pour les impatients



NAME/NOM

perlrequick - Les expressions rationnelles Perl pour les impatients


DESCRIPTION

Ce document décrit les bases nécessaires à la compréhension, la création et l'utilisation des expressions rationnelles ou régulières (abrégé en regex) en Perl.


Le guide

Reconnaissance de mot simple

L'expression rationnelle la plus simple est juste un mot ou, plus généralement, une chaîne de caractères. Une regex constituée d'un mot est reconnue dans (ou correspond avec) toutes les chaînes qui contiennent ce mot :

    "Salut tout le monde" =~ /monde/;  # correspondance

Dans cette instruction, monde est la regex et les // qui l'entourent demandent à perl de rechercher une correspondance dans la chaîne. L'opérateur =~ applique la recherche à la chaîne placée à gauche et produit la valeur vraie si la regex correspond ou la valeur fausse sinon. Dans notre cas, monde correspond au quatrième mot de "Salut tout le monde". Donc l'expression est vraie. Ce concept a plusieurs usages.

Des expressions de ce type sont utiles dans des conditions :

    print "Correspondance\n" if "Salut tout le monde" =~ /monde/;

Le sens de l'expression peut être inversé en utilisant l'opérateur !~ :

    print "Pas de correspondance\n" if "Salut tout le monde" !~ /monde/;

La chaîne littérale dans la regex peut être remplacée par une variable :

    $mot = "monde";
    print "Correspondance\n" if "Salut tout le monde" =~ /$mot/;

Si vous voulez chercher dans $_, la partie $_ =~ peut être omise :

    $_ = "Salut tout le monde";
    print "Correspondance\n" if /monde/;

Finalement, les délimiteurs par défaut // pour une recherche de correspondance peuvent être remplacés par des délimiteurs arbitraires en les préfixant part un 'm' :

    "Salut le monde" =~ m!monde!; # correspondance, délimité par '!'
    "Salut le monde" =~ m{monde}; # correspondance, remarquez la paire '{}'
    "/usr/bin/perl"  =~ m"/perl"; # correspondance après '/usr/bin',
                                  # '/' devient un caractère ordinaire

Les regex doivent correspondre exactement à une partie de la chaîne pour être reconnue :

    "Salut le monde" =~ /Monde/;  # pas de correspondance, casse différente
    "Salut le monde" =~ /e m/;    # correspondance, ' ' est un caractère ordinaire
    "Salut le monde" =~ /monde /; # pas de correspondance, pas de ' ' à la fin

La reconnaissance a lieu le plus tôt possible dans la chaîne :

    "Salut le monde" =~ /l/;    # reconnaît le 'l' dans 'Salut
    "La peste est là" =~ /est/; # reconnaît le 'est' dans 'peste'

Certains caractères ne peuvent être utilisés tels quels dans une regex. Ces caractères, appelés meta-caractères, sont réservés pour des notations spéciales dans les expressions rationnelles. Les voici :

    {}[]()^$.|*+?\

Un meta-caractère peut-être utilisé en le préfixant par un backslash (une barre oblique inversée) :

    "2+2=4" =~ /2+2/;    # pas de correspondance, + est un meta-caractère
    "2+2=4" =~ /2\+2/;   # correspondance, \+ est traité comme un + ordinaire
    'C:\WIN32' =~ /C:\\WIN/;                # correspondance
    "/usr/bin/perl" =~ /\/usr\/bin\/perl/;  # correspondance

Dans ce dernier exemple, le symbole divisé / est aussi préfixé par un backslash car il est utilisé comme délimiteur par l'expression rationnelle.

Les caractères ASCII non affichables sont représentés par des séquences d'échappement. Les exemples courants sont \t pour une tabulation, \n pour un passage à la ligne et \r pour un retour chariot. Les octets quelconques sont représentés par une séquence d'échappement en octal (comme \033) ou en hexadécimal (comme \x1B) :

    "1000\t2000" =~ m(0\t2)            # correspondance
    "chat"       =~ /\143\150\x61\x74/ # correspondance,
                                       # mais 'chat' est écrit bizarrement

Les expressions rationnelles sont traitées quasiment comme des chaînes entre guillemets. Donc l'interpolation des variables fonctionne :

    $foo = 'son';
    'caisson' =~ /cais$foo/; # correspondance
    'sonnet' =~ /${foo}net/; # correspondance

Dans toutes les expressions rationnelles qui précèdent, si la regex est reconnue quelque part dans la chaîne, on considère qu'il y a correspondance. Pour spécifier doit avoir lieu la reconnaissance, vous pouvez utiliser les meta-caractères d'ancrage ^ et $. L'ancre ^ est reconnue au début de la chaîne alors que l'ancre $ est reconnue à la fin de la chaîne ou juste avant une fin de ligne à la fin de la chaîne. Quelques exemples :

    "housekeeper" =~ /keeper/;         # correspondance
    "housekeeper" =~ /^keeper/;        # pas de correspondance
    "housekeeper" =~ /keeper$/;        # correspondance
    "housekeeper\n" =~ /keeper$/;      # correspondance
    "housekeeper" =~ /^housekeeper$/;  # correspondance

Utilisation des classes de caractères

Une classe de caractères définit un ensemble de caractères acceptables en un point particulier de l'expression rationnelle. Une classe de caractères s'exprime par une paire de crochets [...] contenant l'ensemble des caractères acceptables. Voici quelques exemples :

    /rame/;            # reconnaît 'rame'
    /[clr]ame/;        # reconnaît 'came, 'lame' ou 'rame'
    "abc" =~ /[cab]/;  # reconnaît 'a'

Dans la dernière instruction, bien que 'c' soit le premier caractère de la classe, le premier endroit où cette expression rationnelle peut être reconnue est le 'a'.

    /[oO][uU][iI]/; # reconnaît 'oui' indépendamment de la casse
                    # 'oui', 'Oui', 'OUI', etc.
    /oui/i;         # reconnaît aussi 'oui' indépendamment de la casse

Le dernier exemple démontre l'usage du modificateur 'i' qui permet une mise en correspondance indépendante de la casse (majuscule/minuscule).

Les classes de caractères ont elles aussi leurs caractères normaux et spéciaux mais ce ne sont pas les mêmes qu'à l'extérieur d'une classe. Les caractères spéciaux sont -]\^$. Pour les rendre normaux, il faut les préfixer par \ :

   /[\]c]def/; # correspond à ']def' ou 'cdef'
   $x = 'clr';
   /[$x]ame/;  # correspond à 'came, 'lame' ou 'rame'
   /[\$x]ame/; # correspond à '$ame' or 'xame'
   /[\\$x]ame/; # correspond à '\ame', 'came, 'lame' ou 'rame'

Le caractère spécial '-' agit à l'intérieur d'une classe comme un opérateur d'intervalle. Donc les classes peu maniables [0123456789] et [abcde...xyz] deviennent [0-9] et [a-z] :

    /item[0-9]/;   # reconnaît 'item0' ou 'item1' ... ou 'item9'
    /[0-9a-fA-F]/; # reconnaît un chiffre hexadécimal

Si '-' est le premier ou le dernier caractère d'une classe de caractères, il est traité comme un caractère ordinaire.

Le caractère ^ est spécial en première position de la classe. Il indique alors une classe de caractères complémentaire qui reconnaît tous les caractères sauf ceux présents entre les crochets. Qu'elle soit de la forme [...] ou [^...], une classe de caractères doit correspondre à un caractère sinon la reconnaissance échoue. Donc :

    /[^a]at/;  # ne reconnaît ni 'aat' ni 'at', mais reconnaît
               # 'bat', 'cat, '0at', '%at', etc.
    /[^0-9]/;  # reconnaît un caractère non numérique
    /[a^]at/;  # reconnaît 'aat' ou '^at'; dans ce cas '^' est ordinaire

Perl propose plusieurs abréviations pour des classes de caractères courantes :

Les abréviations \d\s\w\D\S\W peuvent être utilisées à l'extérieur ou à l'intérieur d'une classe de caractères. Quelques exemples :

    /\d\d:\d\d:\d\d/; # reconnaît une heure au format hh:mm:ss
    /[\d\s]/;         # reconnaît un chiffre ou un blanc
    /\w\W\w/;         # reconnaît un caractère mot suivi d'un caractère
                      # non mot, suivi d'un caractère mot
    /..rt/;           # reconnaît deux caractères quelconques suivis de 'rt'
    /fin\./;          # reconnaît 'fin.'
    /fin[.]/;         # idem, reconnaît 'fin.'

L'ancre \b est reconnue à la limite de mot : entre un caractère mot et un caractère non mot (entre \w\W ou entre \W\w).

    $x = "Housecat catenates house and cat";
    $x =~ /\bcat/;  # reconnaît cat dans 'catenates'
    $x =~ /cat\b/;  # reconnaît cat dans 'housecat'
    $x =~ /\bcat\b/;  # reconnaît 'cat' en fin de chaîne

Dans le dernier exemple, la fin de la chaîne est considérée comme une limite de mot.

Reconnaître ceci ou cela

Nous pouvons reconnaître différentes chaînes grâce au meta-caractère d'alternative |. Pour reconnaître chien ou chat, nous pouvons utiliser la regex chien|chat. Comme précédemment, perl essayera de reconnaître la regex le plus tôt possible dans la chaîne. À chaque position, perl essayera la première possibilité : chien. Si chien ne correspond pas, perl essayera la possibilité suivante : chat. Si chat ne convient pas non plus alors il n'y a pas correspondance et perl se déplace à la position suivante dans la chaîne. Quelques exemples :

    "chiens et chats" =~ /chien|chat|rat/;  # reconnaît "chien"
    "chiens et chats" =~ /chat|chien|rat/;  # reconnaît "chien"

Bien que chat soit la première possibilité dans la seconde regex, chien est reconnue plus tôt dans la chaîne.

    "chat"          =~ /c|ch|cha|chat/; # reconnaît "c"
    "chat"          =~ /chat|cha|ch|c/; # reconnaît "chat"

En une position donnée, la possibilité qui est retenue est la première qui permet la reconnaissance de l'expression. Ici, toute les possibilités correspondent dès le premier caractère donc c'est la première qui est retenue.

Groupement et hiérarchie

Les meta-caractères de regroupement () permettent de traiter une partie d'une regex comme une seule entité. Une partie d'une regex est groupée en l'entourant de parenthèses. L'expression para(pluie|chute) peut reconnaître para suivi soit de pluie soit de chute. D'autres exemples :

    /(a|b)b/;    # reconnaît 'ab' ou 'bb'
    /(^a|b)c/;   # reconnaît 'ac' au début de la chaîne ou 'bc' n'importe où
    /chat(on|)/;      # reconnaît 'chaton' ou 'chat'.
    /chat(on(s|)|)/;  # reconnaît 'chatons' ou 'chaton' ou 'chat'.
                      # Notez que les groupes peuvent être imbriqués
    "20" =~ /(19|20|)\d\d/;  # reconnaît la possibilité vide '()\d\d',
                             # puisque '20\d\d' ne peut pas correspondre

Mémorisation de la correspondance

Les meta-caractères de regroupement () permettent aussi la mémorisation de la partie reconnue de la chaîne. Pour chaque groupe, la partie reconnue de la chaîne va dans une variable spéciale $1 ou $2, etc. Elles peuvent être utilisées comme des variables ordinaires :

    # extraction des heures, minutes, secondes
    $temps =~ /(\d\d):(\d\d):(\d\d)/;  # reconnaît le format hh:mm:ss
    $heures = $1;
    $minutes = $2;
    $secondes = $3;

Dans un contexte de liste, une mise en correspondance /regex/ avec regroupement retourne la liste des valeurs ($1, $2, ...). Nous pouvons donc écrire :

    ($heures, $minutes, $secondes) = ($temps =~ /(\d\d):(\d\d):(\d\d)/);

Si les regroupements sont imbriqués, $1 sera le groupe ayant la parenthèse ouvrante la plus à gauche, $2 celui ayant la parenthèse ouvrante suivante, etc. Voici une expression rationnelle complexe avec les numéros des variables de groupes indiqués au-dessous :

    /(ab(cd|ef)((gi)|j))/;
     1  2      34

Les références arrières \1, \2... sont associées aux variables $1, $2... Les références arrières sont utilisées dans l'expression rationnelle elle-même :

    /(\w\w\w)\s\1/; # trouve les séquences telles que 'les les' dans la chaîne

$1, $2... ne devraient être utilisé qu'à l'extérieur de l'expression tandis que \1, \2 ne devraient l'être qu'à l'intérieur.

Répétitions et quantificateurs

Les meta-caractères ou quantificateurs ?, * et {} nous permettent de fixer le nombre de répétitions d'une portion de regex. Un quantificateur est placé juste après le caractère, la classe de caractères ou le regroupement à répéter. Ils ont le sens suivant :

Voici quelques exemples :

    /[a-z]+\s+\d*/;  # reconnaît un mot en minuscules suivi d'au moins un blanc
                     # et éventuellement d'un certain nombre de chiffres
    /(\w+)\s+\1/;    # reconnaît la répétition d'un mot de longueur quelconque
    $annee =~ /\d{2,4}/; # s'assure que l'année contient au moins 2 chiffres et
                         # pas plus de 4 chiffres
    $annee =~ /\d{4}|\d{2}/; # meilleure reconnaissance; exclut le cas de 3 chiffres

Ces quantificateurs essayent d'entrer en correspondance avec la chaîne la plus longue possible tout en permettant à la regex d'être reconnue (ils sont gourmands). Donc :

    $x = 'Ce chien est le mien';
    $x =~ /^(.*)(ien)(.*)$/; # correspondance,
                             # $1 = 'Ce chien est le m'
                             # $2 = 'ien'
                             # $3 = ''   (aucun caractère)

Le premier quantificateur .* consomme la chaîne la plus longue possible tout en laissant une possibilité de correspondance pour la regex globale. Le second quantificateur .* n'a plus de caractère disponible donc il est reconnu zéro fois.

Plus de correspondances

Il y a encore quelques détails que vous devez connaître à propos des opérateurs de correspondance. Dans le code

    $motif = 'Seuss';
    while (<>) {
        print if /$motif/;
    }

perl doit réévaluer $motif à chaque passage dans la boucle. Si $motif ne change jamais, utilisez le modificateur //o pour n'effectuer qu'une seule fois l'interpolation. Si vous ne voulez aucune interpolation, utilisez les délimiteurs spéciaux m'' :

    @motif = ('Seuss');
    m/@motif/; # reconnaît 'Seuss'
    m'@motif'; # reconnaît la chaîne littérale '@motif'

Le modificateur //g demande à l'opérateur de mise en correspondance de s'appliquer à une chaîne autant de fois que possible. Dans un contexte scalaire, une recherche de correspondance assortie du modificateur //g sautera de reconnaissance en reconnaissance en se souvenant à chaque fois de l'endroit où elle s'est arrêtée la fois précédente. Vous pouvez récupérer la position atteinte via la fonction pos(). Par exemple :

    $x = "chien chat maison"; # 3 mots
    while ($x =~ /(\w+)/g) {
        print "le mot $1 se termine en ", pos $x, "\n";
    }

affiche

  le mot chien se termine en 5
  le mot chat se termine en 10
  le mot maison se termine en 17

L'échec de la reconnaissance ou la modification de la chaîne réinitialise la position. Si vous ne voulez pas de réinitialisation en cas d'échec, ajoutez le modificateur //c comme dans /regex/gc.

Dans un contexte de liste, //g retournera la liste complète des groupes reconnus ou, si il n'y a pas de regroupement, la liste des sous-chaînes reconnues par la regex complète. Donc :

    @mots = ($x =~ /(\w+)/g);  # correspondance,
                               # $mots[0] = 'chien'
                               # $mots[1] = 'chat'
                               # $mots[2] = 'maison'

Recherche et remplacement

On effectue une recherche et remplacement en utilisant s/regex/remplacement/modificateurs. remplacement est comme une chaîne Perl entre guillemets qui remplacera dans la chaîne la partie reconnue par la regex. Là aussi, l'opérateur =~ permet de choisir à quelle chaîne sera appliquée s///. Si s/// doit s'appliquer à $_, il est possible d'omettre $_ =~. S'il y a une correspondance, s/// retourne le nombre de remplacements effectués sinon il retourne faux. Voici quelques exemples :

    $x = "Time to feed the cat!";
    $x =~ s/cat/hacker/;   # $x contient "Time to feed the hacker!"
    $y = "'quoted words'";
    $y =~ s/^'(.*)'$/$1/;  # supprime les apostrophes,
                           # $y contient "quoted words"

Lorsqu'on utilise l'opérateur s///, les variables $1, $2, etc. sont directement utilisables dans l'expression de remplacement. Avec le modificateur s///g, la recherche et remplacement auront lieu sur toutes les occurrences de l'expression rationnelle :

    $x = "I batted 4 for 4";
    $x =~ s/4/four/;   # $x contient "I batted four for 4"
    $x = "I batted 4 for 4";
    $x =~ s/4/four/g;  # $x contient "I batted four for four"

Le modificateur d'évaluation s///e ajoute un eval{...} autour de la chaîne de remplacement et c'est le résultat de cette évaluation qui sera substitué à la sous-chaîne reconnue. Quelques exemples :

    # inverser tous les mots d'une chaîne
    $x = "the cat in the hat";
    $x =~ s/(\w+)/reverse $1/ge;   # $x contient "eht tac ni eht tah"

    # convertir un pourcentage en fraction
    $x = "A 39% hit rate";
    $x =~ s!(\d+)%!$1/100!e;       # $x contient "A 0.39 hit rate"

Le dernier exemple montre que s/// peut utiliser d'autres délimiteurs tels que s!!! ou s{}{}... ou même s{}//. Si les délimiteurs sont des apostrophes s''' alors l'expression rationnelle et la chaîne de remplacement sont considérées comme des chaînes entre apostrophes (pas d'interpolation des variables).

L'opérateur de découpage : split

split /regex/, chaine découpe chaine en une liste de sous-chaînes et retourne cette liste. La regex détermine la séquence de caractères à utiliser comme séparateur lors du découpage de string. Par exemple, pour découper une chaîne en mots, utilisez :

    $x = "Calvin and  Hobbes";
    @mots = split /\s+/, $x;  # $mots[0] = 'Calvin'
                              # $mots[1] = 'and'
                              # $mots[2] = 'Hobbes'

Pour extraire une liste de nombres séparés par des points-virgules :

    $x = "1,618;2,718;   3,142";
    @const = split /;\s*/, $x;  # $const[0] = '1,618'
                                # $const[1] = '2,718'
                                # $const[2] = '3,142'

Si vous utilisez l'expression rationnelle vide //, la chaîne est découpée en ses caractères. Si l'expression rationnelle contient des regroupements alors la liste produite contiendra aussi les groupes reconnus :

    $x = "/usr/bin";
    @parts = split m!(/)!, $x;  # $parts[0] = ''
                                # $parts[1] = '/'
                                # $parts[2] = 'usr'
                                # $parts[3] = '/'
                                # $parts[4] = 'bin'

Puisque le premier caractère de $x est reconnu comme délimiteur, split ajoute un élément vide au début de la liste.


BUGS

Aucun.


VOIR AUSSI

C'est un simple guide d'introduction. Pour un tutoriel plus complet voir la page de manuel perlretut et pour une référence complète voir la page de manuel perlre.


AUTEUR ET COPYRIGHT

Copyright (c) 2000 Mark Kvale All rights reserved.

This document may be distributed under the same terms as Perl itself.

Tous droits réservés.

Ce document peut être distribuer sous les mêmes termes que Perl.

Remerciements

L'auteur tient à remercier Mark-Jason Dominus, Tom Christiansen, Ilya Zakharevich, Brad Hughes et Mike Giroux pour leurs commentaires précieux.


TRADUCTION

Version

Cette traduction française correspond à la version anglaise distribuée avec perl 5.10.0. Pour en savoir plus concernant ces traductions, consultez http://perl.enstimac.fr/.

Traducteur

Paul Gaborit (Paul.Gaborit at enstimac.fr).

Relecture

Aucune pour l'instant.