Perl, les expressions régulières

Sébastien Aperghis-Tramoni, sebastien@aperghis.net

Expressions régulières

  • Perl has often been tagged as a language in which it's easy to write programs that are difficult to read, and it's no secret that regular expression syntax that has been the chief culprit. Funny that other languages have been borrowing Perl's regular expressions as fast as they can...

    -- Larry Wall, Apocalypse 5

Terminologie

  • expressions rationnelles

  • regular expressions => regexp

  • => expressions régulières

Théorie

  • théorie des automates et langages formels

  • grammaires de recherche de correspondances

    • NFA : nondeterministic finite automaton

    • DFA : deterministic finite automaton

Historique

  • UNIX : qed, ed, grep

  • POSIX regex

  • Perl 5, PCRE

  • Perl 6

Fonctionnement

  • correspondance de caractères

  • motif composé de :

    • caractères normaux

    •     "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éta-caractères : {}[]()^$.|*+?\

    •     "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éta-caractères : {}[]()^$.|*+?\

    • séquences d'échappement

    •     "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 possible pour un emplacement de caractère

  • notées par [...]

  •     /[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 possible pour un emplacement de caractère

  • notées par [...]

  • intervalles de caractères

  •     /[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 : [^...]

  •     /[^0-9]/;   # cherche un caractère qui n'est pas un chiffre

Classes de caractères

  • classes prédéfinies :

    • . : tout caractère sauf \n

    • \d : chiffre décimal

    • \w : caractère de mot (alphanumérique plus _)

    • \s : espace normale, tabulation, saut de ligne

    • \h : espace horizontal

    • \v : espace vertical

    • \R : saut de ligne

Classes de caractères

  • 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 un espace usuel

    • \H : ce qui n'est pas un espace horizontal

    • \V : ce qui n'est pas un espace vertical

Classes de caractères

  • exemples :

    •     /\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 chaine

  •     "beausoleil" =~ /^soleil/;      # ne correspond pas

Ancres

  • pour ancrer la recherche dans certains points

  • ^ : en début de chaine

  • $ : en fin de chaine

  •     "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 chaine

  • $ : en fin de chaine

  • \b : frontière de mot, intervient entre \w et \W

  •     "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

Quantifieurs

  • exemples :

  •     "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

  • 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

Quantifieurs

  • exemples :

  •     "aaaa" =~ /a+?/;    # correspond avec "a"
  •     "aaaabbbb" =~ /a+?b*?/;     # correspond avec "a"
  •     "aaaabbbb" =~ /a+?b+?/;     # correspond avec "aaaab"

Quantifieurs

  • 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

Quantifieurs

  • exemples :

  •     "aaaa" =~ /a+a/;   # /a+/ correspond avec "aaa"
  •     "aaaa" =~ /a+?a/;  # /a+?/ correspond avec "a"
  •     "aaaa" =~ /a++a/;  # ne correspond pas

Quantifieurs

  • résumé :

  •                             "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ées par \1, \2... dans le motif

  • référencées par $1, $2... à l'extérieur du motif

  • groupes non-capturant : (?:...)

Groupes

  • exemples :

  •     /(\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 :

  •     /^(add|del|list) +(addr|route|rule|table) .../
            # "add addr ..."
            # "del rule ..."
            # "list route ..."

Captures et alternatives

  • problème de la numérotation des captures

  •     / ( a )  (?: x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
        # 1            2         3  4        5     6     7

Captures et alternatives

  • remise à zéro de branche : (?|..)

  • numérote les captures des branches d'une alternative comme s'il n'y en avait qu'une seule

  •     # 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

        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 en Perl :

        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

Utilisation

  • m// - recherche de correspondances

  •     $text = "whack zlott ooooff kraaack zzzzzwap";
    
        $text =~ /(kra+ck)/;
    
        print $1;               # "kraaack"
  •     $line = "peer_list = host1 host2 host3";
    
        $line =~ m/^(\w+) *= *(.*)$/;
    
        print $1;               # "peer_list"
        print $2;               # "host1 host2 host3"

Utilisation

  • s/// - recherche et remplacement

  •     $source =~ s/OpenOffice.org/LibreOffice/g;
  •     $World =~ s/war/peace/g;

Utilisation

  • s/// - recherche et remplacement

  •     $text = "The quick quick brown fox jump jump over the lazy dog.";
  •     $text =~ s/(\w+) +\1/$1/;

Assertion keep

  • Regexp::Keep de Jeff Pinyan sur le CPAN

  • préserve la partie à gauche de \K

  • s/(save)delete/$1/ devient s/save\Kdelete//

  • identique mais bien plus rapide

    • fonctionne par simple déplacement de pointeur, au lieu de réaliser des sauvegardes et copies de chaînes

Utilisation

  • split, join :

  •     $line = "saper:500:500:Sébastien Aperghis-Tramoni:/home/saper:/bin/bash";
        @fields = split /:/, $line;
        print $fields[5];       # "/bin/bash"
  •     $fields[3] = "Groucho Marx";
        $line = join ":", @fields;
        print $line;    # "saper:500:500:Groucho Marx:/home/saper:/bin/bash"
  •     @flags = qw( UP BROADCAST SMART RUNNING SIMPLEX MULTICAST );
        print join ", ", @flags;
                    # "UP, BROADCAST, SMART, RUNNING, SIMPLEX, MULTICAST"

Lecture

Lecture

Lecture

  • Perl moderne

  • expressions régulières actuelles

  • objet moderne avec Moose

  • bases de données avec DBI

  • programmation événementielle avec POE

  • navigation sur le web avec LWP et WWW::Mechanize

  • http://perlmoderne.fr/