Expressions régulières

 

I. Introduction

Les expressions régulières sont présentes dans les langages qui font une grande place aux traitements de chaînes de caractères. Citons les interpréteurs de commandes unix (sh, csh, ksh, bash ..), Perl, Php ... et Javascript.

Les expressions régulières servent à détecter des motifs dans un texte. Par exemple on peut chercher la présence d'une date écrite 13/03/2003 . Il n'est pas question de trouver uniquement cette date, mais de trouver les écritures qui 'ressemblent' à celle là. On va donc se baser sur un motif qui décrit ce que l'on cherche; on appelle correspondance la partie du texte qui satisfait le motif.

Pour cet exemple de recherche de date, le motif est: \d\d/\d\d/\d\d\d\d ; sachant que \d représente un chiffre, et qu'on a repéré '/' parmi ces chiffres,le motif s'explique ainsi:
suite de caractères constituée par : 2 chiffres / 2 chiffres / 4 chiffres

Dans le texte 'congé entre le 25/04/2003 et 02/05/2003' nous avons deux correspondances associées au motif précédent, qui sont 25/04/2003 et 02/05/2003.

Les expressions régulières sont utilisées pour vérifier qu'un champ est rempli correctement, c'est à dire vérifie une certaine syntaxe; cela permet, par exemple, de contrôler une adresse électronique.

Un petit langage, dont les conventions sont résumées ci-après , permet de décrire les motifs.

La recherche de motif dans un texte peut fournir plusieurs résultats: Deux autres fonctionalités utilisent les expressions régulières:

 

II. Exemples d'expressions régulières

On commence par des cas simples, puis des expressions plus complexes, faisant intervenir des facteurs de répétition ; dernier exemple : adresse email
 
  1. un texte: Jean:23/11/1980, Paul:25/12/1979, Yann:01/05/1982
    avec lequel nous présentons les résultats de recherche de divers motifs.
    expression interprétation correspondance(s) composant(s)
    Paul recherche d'un mot Paul   vide
    198[0-9] dans les années 1980 1980   1982 vide
    25/12/([0-9]*) un jour de Noël 25/12/1979 1979
    Précisons qu'un composant est associé à une partie d'une expression régulière délimitée par les parenthèses; cette partie est appelée atome. S'il n'y a pas de parenthèses, donc pas d'atome, il n'y a pas de composant.

  2. ^[0-9]{10}$
    permet de s'assurer qu'un champ représente un numéro de téléphone constitué de 10 chiffres
    ^ commence par
    [0-9] indique tout caractère dont le code est compris entre celui de 0 et celui de 9, soit un chiffre
    {} les accolades introduisent un nombre de répétitions
    10 exactement 10 fois un chiffre
    $ à la fin du champ
    Ainsi le champ contient 10 chiffres décimaux exactement.

  3. ^0[1-9][0-9]{8}$
    un peu plus de précision avec cette autre expression
    ^0 commence par le chiffre 0 - c'est très précis
    [1-9] suivi d'un chiffre entre 1 et 9 - le deuxième chiffre
    [0-9]{8} suivi de 8 chiffres entre 0 et 9
    $ qui terminent le numéro de téléphone
    Ainsi comme précédemment le champ contient aussi 10 chiffres, mais la description du début est précise.

  4. [a-zA-Z]+
    permet de s'assurer de la présence d'au moins une lettre, par exemple pour de vérifier qu'un champ nom a été renseigné.
    [a-zA-Z] indique tout caractère dont le code est compris entre celui de a et celui de z ou entre celui de A ou de Z, soit une lettre
    + indique la présence d'au moins une occurence de l'entité précédente

  5. ^[ ]*[-+]?[0-9]+[ ]*$
    décrit tout champ ne contenant qu'une valeur numérique entière comme -83 par exemple, en tolérant des espaces avant ou après la valeur. On analyse ce modèle comme suit:
    ^[ ]* commence par 0 ou des espaces,
    [-+] continue par le signe + ou - *
    ? apparaissant 0 ou 1 fois,
    [0-9]+ suivi par au moins un chiffre,
    [ ]* suivi par 0 ou plusieurs espaces
    $ qui terminent l'expression

  6. \b[A-Z_a-z]\w*\b
    décrit un identificateur usuel dans un langage de programmation:
    \b le mot commence
    [A-Z_a-z] par une lettre ou un soulignement
    \w* se continue par 0 ou plusieurs lettres, chiffres, soulignements
    \b qui terminent le mot

  7. ^[_a-z0-9-]+([.][_a-z0-9-]+)*@([a-z0-9][_a-z0-9-]*[.])+([a-z]{2,6})$
    décrit une adresse e-mail, en ignorant la distinction majuscules/minuscules :
    ^[_a-z0-9-]+ commence par des lettres, chiffres, tirets ou soulignements
    ([.][_a-z0-9-]+)* se poursuit pas 0 ou plusieurs groupes commençant par un .
    suivit de lettres,chiffres,tirets,soulignements
    @ puis l'arobase @
    ([a-z0-9][_a-z0-9-]*[.])+ puis un ou plusiers mots commençant par lettre ou chiffre, suivi de lettres,chiffres,tirets,soulignements
    terminé par un . (un ou plusieurs sous-domaines)
    ([a-z]{2,6})$ puis un mot entre 2 et 6 lettres (nom de domaine de premier niveau) qui termine l'adresse

 

III. Langage d'écriture des expressions régulières

Nous présentons en premier les possibilités, relativement standardisées, utilisées dans tous les langages, puis nous mentionnons les particularités de javascript.
  1. Indiquer qu'un caractère, parmi un sous-ensemble, est dans le motif.
     
    Classe de caractères
    . tout caractère sauf \n
    [abcd] une des lettres a, b, c ou d
    [a-zA-Z] tout caractère entre a et z ou entre A et Z
    [^0-9] tout caractère autre que chiffre
    ^ est placé juste après le crochet ouvrant, puis on indique les caractères qui ne doivent pas figurer dans le motif.

    Remarquez le sens spécial du '.' (en dehors des crochets), des caractères '[' et ']', du signe '-' et du signe '^' placé juste après un crochet ouvrant.
    Si le signe - doit être dans le motif, le placer juste après le crochet ouvrant, comme par exemple [-+*/] , ou juste avant le crochet fermant.

  2. Indiquer qu'une entité est en début ou en fin de chaîne.
     
    Position
    ^ début de chaîne si placé en première position
    $ fin de chaîne si placé en dernière position

  3. Indiquer qu'une entité est répétée.
    Si on cherche par exemple   bbbbbbbbbb   c'est à dire 10 fois b, on a la possibilité d'indiquer 10 comme facteur de répétition. On peut moduler ce facteur.
    Répétition
    b{2} 2 fois b ou bb
    b(1,3) entre 1 et 3 b
    b{2,} au moins 2 b
    b* 0 ou plusieurs b (<==> b{0,})
    b+ 1 ou plusieurs b (<==> b{1,})
    b? 0 ou 1 b (<==> b{0,1})
    a\*c n'est pas une répétition, mais représente les trois caractères a * c car * ayant une interprétation spéciale comme indication de répétition, si on désire chercher le caractère * dans le texte, il doit être précédé de \ dans l'expression régulière.


  4. Indiquer que le motif peut avoir une forme ou une autre
    Alternative
    en (mars|avril) correspondance trouvée dans:   floraison en mars
    correspondance trouvée dans:   floraison en avril
    pas de trouvée dans:   floraison en mai
       

  5.  
    Caractères spéciaux
    Tous les caractères   * + ? . ( ) [ ] { } | \   que nous venons de voir ont un sens spécial.

  6.   Les particularités suivantes tiennent surtout à la syntaxe, plus qu'au concept. Par exemple il existe une notation particulière pour la classe des chiffres [0-9], qui est \d en javascript ou Php, et [:digit:] pour les langages de commande d'Unix.
     
    Particularités Javascript
    \f  \n  \r saut de page, saut de ligne, début de ligne
    \t  \v tabulation, tabulation verticale
    \cX caractère de contrôle ^X (générique)
    \d désigne la classe chiffre <==> [0-9]
    \D pas un chiffre <==> [^0-9]
    \s espace, ou tabulation ... <==> [\f\n\r\t\v]
    \S inverse du précédent <==> [^\f\n\r\t\v]
    \w lettre, soulignement ou chiffre <==> [A-Za-z0-9_]
    \W inverse du précédent <==> [^A-Za-z0-9_]
    \b frontière de mot
    \B en dehors d'une frontière de mot

    Modifications sur la recherche
    Deux possibilités supplémentaires: d'une part demander que les comparaisons sur les lettres ne tiennent pas compte de la différence majuscule/minuscule, et d'autre part demander que la recherche continue après avoir trouvé la première occurrence.
    Ces précisions sont fournies, à l'aide d'une, ou deux lettres, placées soit à la fin de l'expression régulière déclarée sous forme explicite, soit comme deuxième argument du constructeur d'un objet RegExp.
    Ces lettres sont:
    i pour ignorer la distinction majuscule/minuscule
    g pour que la recherche soit globale dans le texte
    Exemples:
    /jean/i permet de trouver jean ou Jean, ... mais aussi jeaN
    new RegExp("jean","gi") recherche globale, sans distinction majuscule/minuscule
 

IV. Quelques fonctions

Avant de voir les fonctions test et exec de la classe RegExp, et search, match, split, replace de la classe String, nous voyons deux exemples de déclaration et d'utilisation d'expressions régulières.

En Javascript une expression régulière peut être obtenue comme une constante littérale, encadrée par des '/', ou comme objet RegExp:

Le premier exemple utilise la fonction test de la classe RegExp.

  var er=/198[0-9]/     /* objet RegExp implicite */
  var texte=new String('Jean:23/11/1983, Paul:25/04/1979')
  if( er.test(texte) )
     document.write("Quelqu'un est né dans la décennie 1980")
  else
    document.write("Pas de naissance dans la décennie 1980")
  
Le deuxième exemple utilise cette fois une autre déclaration de l'expression régulière, etla fonction search de la classe String, pour déterminer l'existence d'une correspondance dans un texte.
  var er= new RegExp("198[0-9]")  /* déclaration explicite */
  var texte='Jean:23/11/1983, Paul:25/12/1979'
  if( texte.search(er) != -1 )
    document.write("Quelqu'un est né dans la décennie 1980")
  else
    document.write("Pas de naissance dans la décennie 1980")
Signalons enfin qu'en Javascript l'objet prédéfini RegExp contient des champs statiques établies ou actualisés dès qu'une expression régulière est utilisée; signalons:
$1, .. $9, lastMatch, lastParen, leftContext, rightContext. A ce sujet, les comportements de Internet Explorer et Netscape peuvent être différents, suivant la version utilisée.

Dans les présentations ci-dessous, vous pouvez fournir une expression régulière, un texte et utilisez le bouton pour activer l'appel d'une fonctions; le résultat est alors affiché.

  1. test, méthode de la classe RegExp
    prototype: function RegExp.test(String) renvoie true|false
    C'est la fonction la plus simple à utiliser; elle renvoie true ou false suivant que motif est trouvé dans la chaîne ou non.
    chaîne:
    expr:         casse ignorée

    Cliquez pour l'appel

  2. exec , méthode de la classe RegExp
    prototype: function RegExp.exec(String) renvoie String[]
    exemple:
          texte = "et abc:12, d:5"
          er = /\s([a-z]+):([0-9]+),/
          res = er.exec(texte)

    Dans cet exemple, l'expression régulière \s([a-z]+):([0-9]+), comprend 2 atomes, qui sont les parties délimitées par les parenthèses, c'est à dire respectivement [a-z]+ et [0-9]+ .
    Le tableau res contient après exécution:

    res[0] :   la correspondance trouvée
    res[1] :   le nom, composant associé à l'atome ([a-z]+)
    res[2] :   le nombre, composant associé à l'atome ([0-9]+)
    res.index :   l'indice de la première correspondance dans texte

    Remarque: si on veut continuer l'analyse de texte on sait que l'indice dans texte qui suit la correspondance trouvée est:
          res.index+res[0].length

    chaîne:
    expr:     global     casse ignorée

    Appel   :  

  3. search, méthode de la classe String
    prototype: function String.search(RegExp) renvoie indice ou -1
    Cette fonction, permet d'avoir l'indice de la première correspondance associée au motif, dans une chaîne.
    chaîne:     expr:

    Cliquez pour l'appel


  4. match, méthode de la classe String
    prototype: function String.match(RegExp) renvoie String[]
    Cette fonction est analogue à exec, et fournit comme résultat, le tableau des sous-chaînes qui correspondent au motif.
    chaîne:
    expr:     global     casse ignorée

    Cliquez pour l'appel
  5. split, méthode de la classe String
    prototype: function String.split(RegExp,maxOccur) renvoie String[]
    Cette fonction permet d'avoir des parties d'un texte, définies par des séparateurs. Le deuxième argument, optionnel, limite la recherche aux premières occurrences trouvées.
    chaîne:
    expr:         casse ignorée

    Cliquez pour l'appel

     


  6. replace, méthode de la classe String
    prototype: deux formes d'appels possibles
        function String.replace(RegExp,chaine2) renvoie String
        function String.replace(RegExp,Function) renvoie String
    Le paramètre chaine2 peut contenir les champs suivants de l'objet RegExp: $1, .. $9, lastMatch, lastParen, leftContext, rightContext; à ce sujet, les comportements de Internet Explorer et Netscape peuvent être différents, suivant la version utilisée.
    texte :
    expr :     global   casse ignorée
    chaine2 :

    Cliquez pour l'appel
 

Références

Quelques adresses pour l'étude des expressions régulières.