Developpez.com

Plus de 2 000 forums
et jusqu'à 5 000 nouveaux messages par jour

Introduction aux expressions régulières de Perl 5 et PCRE

Journées Perl 2014

Petit cours sur les expressions régulières de Perl 5 et PCRE.

3 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction aux expressions régulières

Théorie

  • Théorie des automates et langages formels.
  • Grammaires de recherche de correspondances :

    • DFA : Deterministic Finite Automaton ;
    • NFA : Nondeterministic Finite Automaton.
  • Principale différence : la gestion du retour en arrière (backtracking).

Historique

  • UNIX : qed, ed, grep (BRE, puis ERE).
  • POSIX regex (BRE, ERE).
  • Perl 5, PCRE, Onigurama, (RE2 (DFA amélioré)).
  • Perl 6.

Fonctionnement

  • Correspondance de caractères.
  • Motif composé de :

    • caractères normaux.
 
Sélectionnez
"Hello World" =~ /World/;   # correspond
"Hello World" =~ /lo Wo/;   # correspond aussi
"That hat is red" =~ /hat/; # le "hat" de "That" correspond

Fonctionnement

  • Correspondance de caractères.
  • Motif composé de :

    • caractères normaux ;
    • métacaractères : {}[]()^$.|*+?\.
 
Sélectionnez
"2+2=4" =~ /2+2/;   # ne correspond pas
"2+2=4" =~ /2\+2/;  # correspond

"/usr/bin/perl" =~ /\/usr\/bin\/perl/;

"/usr/bin/perl" =~ m{/usr/bin/perl};

Fonctionnement

  • Correspondance de caractères.
  • Motif composé de :

    • caractères normaux ;
    • métacaractères : {}[]()^$.|*+?\ ;
    • séquences d'échappement.
 
Sélectionnez
"1000\t2000" =~ /00\t20/;   # correspond

"cat" =~ /\143\x61\x74/;# correspond aussi (même si c'est bizarre)

Classes de caractères

  • Ensemble de caractères possibles pour un emplacement de caractère.
  • Notées par […].
 
Sélectionnez
/[bcr]at/;  # cherche "bat", "cat", "rat"

/[yY][eE][sS]/; # cherche "yes", "Yes", YES", etc.

/yes/i; # pareil, mais plus lisible

Classes de caractères

  • Ensemble de caractères possibles pour un emplacement de caractère.
  • Notées par […].
  • Intervalles de caractères.
 
Sélectionnez
/[0-9]/;# équivalent à /[0123456789]/

/[a-z]/;# équivalent à /[abcde...xyz]/

/[0-9a-fA-F]/;  # chiffre hexadécimal

/item[0-9]/;# correspond à "item0", "item1"...

Classes de caractères

  • Ensemble de caractères possibles pour un emplacement de caractère.
  • Notées par […].
  • Intervalles de caractères.
  • Négation de classe : [^...].
 
Sélectionnez
/[^0-9]/;   # cherche un caractère qui n'est pas un chiffre

Classes de caractères

  • Classes prédéfinies :

    • . : tous caractères sauf \n ;
    • \d : chiffre décimal ;
    • \w : caractère de mot (alphanumérique plus _) ;
    • \s : espace normale, tabulation, saut de ligne ;
    • \h : espace horizontale ;
    • \v : espace verticale ;
    • \R : saut de ligne.

  • Classes négatives prédéfinies :

    • \D : ce qui n'est pas un chiffre décimal ;
    • \W : ce qui n'est pas un caractère de mot ;
    • \S : ce qui n'est pas une espace usuelle ;
    • \H : ce qui n'est pas une espace horizontale ;
    • \V : ce qui n'est pas une espace verticale.

Classes de caractères

  • Exemples :
 
Sélectionnez
/\d\d:\d\d:\d\d/;   # format d'heure hh:mm:ss

/[-+]\d/;   # correspond à +2, -3, etc.

/end\./;# correspond à "end."
/end[.]/;   # pareil

Ancres

  • Pour ancrer la recherche dans certains points.
  • ^ : en début de chaîne.
 
Sélectionnez
"beausoleil" =~ /^soleil/;  # ne correspond pas

Ancres

  • Pour ancrer la recherche dans certains points.
  • ^ : en début de chaîne.
  • $ : en fin de chaîne.
 
Sélectionnez
"beausoleil" =~ /beau$/;# ne correspond pas

"beausoleil" =~ /soleil$/;  # correspond

"beausoleil\n" =~ /soleil$/;# correspond aussi

Ancres

  • Pour ancrer la recherche dans certains points.
  • ^ : en début de chaîne.
  • $ : en fin de chaîne.
  • \b : frontière de mot, intervient entre \w et \W.
 
Sélectionnez
"beausoleil" =~ /\bbeau/;   # correspond
"beausoleil" =~ /\bbeau\b/; # ne correspond pas
"beausoleil" =~ /\bsoleil/; # ne correspond pas

Quantifieurs

  • Répétition d'un sous-motif :

    • * : zéro ou plusieurs fois ;
    • + : une ou plusieurs fois ;
    • ? : zéro ou une fois ;
    • {n} : exactement n fois ;
    • {n,} : au moins n fois ;
    • {n,m} : entre n et m fois.
  • Exemples :
 
Sélectionnez
"kraaack" =~ /kra+ck/;  # correspond
"kraaack" =~ /kra{1,}ck/;   # correspond aussi
"kraaack" =~ /kra{5,}ck/;   # ne correspond pas
/\w+\d{2}/; # "item04", "machine42", "Kevin68", etc.
/\d+\.\d+\.\d+\.\d+/;   # recherche simple d'une adresse IPv4
/<[-.\w]+\@[-.\w]+>/;   # recherche simple d'une adresse mail
"aaaa" =~ /a+/; # correspond avec "aaaa"
  • Quantifieurs non avides :

    • *? : zéro ou plusieurs fois, au plus tôt ;
    • +? : une ou plusieurs fois, au plus tôt ;
    • ?? : zéro ou une fois, au plus tôt ;
    • {n}? : exactement n fois, au plus tôt ;
    • {n,}? : au moins n fois, au plus tôt ;
    • {n,m}? : entre n et m fois, au plus tôt.

  • Exemples :
 
Sélectionnez
"aaaa" =~ /a+?/;# correspond avec "a"
 ^

"aaaabbbb" =~ /a+?b*?/; # correspond avec "a"
 ^

"aaaabbbb" =~ /a+?b+?/; # correspond avec "aaaab"
 ^^^^^
  • Quantifieurs possessifs (nouveauté de Perl 5.10 et PCRE 7) :

    • *+ : zéro ou plusieurs fois, et ne rend jamais ;
    • ++ : une ou plusieurs fois, et ne rend jamais ;
    • ?+ : zéro ou une fois, et ne rend jamais ;
    • {n}+ : exactement n fois, et ne rend jamais ;
    • {n,}+ : au moins n fois, et ne rend jamais ;
    • {n,m}+ : entre n et m fois, et ne rend jamais.

  • Exemples :
 
Sélectionnez
"aaaa" =~ /a+a/;   # /a+/ correspond avec "aaa"
 ^^^

"aaaa" =~ /a+?a/;  # /a+?/ correspond avec "a"
 ^

"aaaa" =~ /a++a/;  # /a++/ ne correspond pas
  • Résumé :
 
Sélectionnez
                            "Lorem ipsum dolor sit amet"
  recherche avide :          <<--| <<--| <<--| <<<<<--|
  recherche non avide :      |--->>> |---> |--->> |--->
  recherche possessive :     |---| |---| |---| |------|

Groupes

  • Pour grouper des sous-motifs : (…).
  • Par défaut, groupes capturant.
  • Référencés par \1, \2… dans le motif.
  • Référencés par $1, $2… en dehors du motif.
  • Groupes non capturant : (?:…).

  • Exemples :
 
Sélectionnez
/(\w+\d+ )+/;   # "item04 machine42 Kevin68 "

/(?:\d+\.){3}\d+/; # recherche simple d'une adresse IPv4

/((?:\d+\.){3}\d+)/;# recherche simple d'une adresse IPv4

/(\w+) +\1/;# recherche d'un mot répété

Alternatives

  • Disjonction de sous-motifs au sein d'un groupe.
  • Exemple :
 
Sélectionnez
/^(add|del|list) +(addr|route|rule|table) .../
# "add addr ..."
# "del rule ..."
# "list route ..."

Captures et alternatives

  • Problème de la numérotation des captures.
 
Sélectionnez
    / ( a )  (?: x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
    # 1            2         3  4        5     6     7
  • Remise à zéro de branche : (?|..).
  • Numérote les captures des branches d'une alternative comme s'il n'y en avait qu'une seule.
 
Sélectionnez
    # before  ------------branch-reset-------------  after
    / ( a )  (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
    # 1            2         2  3        2     3     4

Captures numérotées

  • Nouvelle syntaxe de référencement : \g{N}.
  • N positif : numéro de capture usuel.
  • N négatif : référencement arrière relatif.
  • \g{-1} == précédente capture.
  • Exemple :
 
Sélectionnez
my $find_dup = qr/ (\w+) \s+ \g{-1} /x;

Captures nommées

  • (?<name>pattern) pour nommer une capture.
  • \k<name>, \k{name} ou \g{name} pour s'y référer.
  • Exemple :
 
Sélectionnez
my $find_dup = qr/ (?<dup_word>\w+) \s+ \k<dup_word> /x;

Captures nommées

  • Variables lexicales %+ et %-.
  • $+{name} == \g{name}.
  • $-{name} == référence vers un tableau de toutes les captures de ce nom.
  • Tie::Hash::NamedCapture pour avoir des noms plus clairs.

Non abordés

  • Modificateurs : /i, /m, /s, /x, /p, /r, /g, /c, /a, /d, /l, /u.
  • Assertion keep, \K.
  • Assertion \G.
  • Assertions de longueur nulle, de recherche alentour positive ou négative : (?=..), (!..), (?<=..)>, (?<!..).

Non abordés

  • Ensembles de classes (?[...]).
  • Évaluation de code à la volée : (?{ code }), (??{ code }).
  • Verbes de contrôle : (*PRUNE), (*SKIP), (*THEN), (*COMMIT), (*FAIL), (*ACCEPT).
  • Unicode.

Motifs récursifs

  • Possibles en Perl 5.8, mais de manière horrible.
  • Syntaxe mise au propre dans PCRE.
  • Principe : réinvocation d'un groupe capturant avec (?PARNO).
  • PARNO == numéro de parenthèse (groupe capturant).
  • Si précédé d'un signe, compris de manière relative.

Motifs récursifs

  • (?2) => le 2e groupe déclaré.
  • (?-1) => dernier groupe déclaré.
  • (?+1) => prochain groupe qui sera déclaré.
  • (?0) ou (?R) pour invoquer à nouveau le motif complet.
  • (?&name) => invoque un groupe nommé.

Motifs récursifs

  • Reconnaissance de parenthèses imbriquées :
 
Sélectionnez
  $s  = "( crack ( kapow ) ( klang ) ouch )";

  $re = qr{ (           # groupe #1
                \(             # parenthèse ouvrante
                (?:
                      (?> [^()]+ )    # groupe sans retour arrière
                  |
                      (?1)            # groupe avec parenthèses 
                )*
                \)             # parenthèse fermante
              )
          }x;

Motifs récursifs

  • (?(condition)yes-pattern|no-pattern) => construction conditionnelle, accepte :

    • un numéro de groupe (1), (2)
    • un nom de groupe <name>,
    • un bout de code Perl (?{ CODE }),
    • (R) pour vérifier si évaluée au sein d'une récursion,
    • avec numéro ((R1), (R2)..) ou nom ((R&name)) d'un groupe pour vérifier si évaluée pendant l'exécution du groupe.

Motifs récursifs

  • Cas particulier : (DEFINE).
  • Seule la partie yes-pattern est autorisée.
  • N'est pas directement exécutée.
  • Mais peut entrer dedans en récursion.
  • Permet donc de définir des fonctions de regexps.

Motifs récursifs

 
Sélectionnez
my $log = "192.168.1.11 -> 192.168.1.12 connect tcp 80";

 $log =~ m{
         (?<src_addr>(?&ip_addr))    # adresse IP source
         \s+ -> \s+
         (?<dest_addr>(?&ip_addr))   # adresse IP destination

         (?(DEFINE)
                 (?<ip_addr>...)     # motif pour reconnaître une 
         )                       # addresse IP
 }x

En ligne

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Licence Creative Commons
Le contenu de cet article est rédigé par Sébastien Tramonis-Asperghis et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.