=encoding iso-8859-1 =head1 NAME/NOM perlretut - Tutoriel des expressions rationnelles en Perl =head1 DESCRIPTION BE: on emploie couramment le terme "expression régulière" car le terme anglais est "regular expression" qui s'abrège en "regexp". Mais ne nous y trompons pas, en français, ce sont bien des "expressions rationnelles". Ce document propose un tutoriel dans le but de comprendre, créer et utiliser des expressions rationnelles en Perl. Il sert de complément à la documentation de référence sur les expressions rationnelles, L. Les expressions rationnelles font partie intégrante des opérateurs C, C, C et C, donc ce tutoriel a aussi des recoupements avec L et L. Perl est largement reconnu pour ses capacités de manipulation de textes et les expressions rationnelles y sont pour beaucoup. Les expressions rationnelles en Perl permettent une flexibilité et une efficience inconnues dans la plupart des autres langages. La maîtrise des expressions rationnelles même les plus simples vous permettra de manipuler du texte avec une surprenante facilité. Qu'est-ce qu'une expression rationnelle ? Une expression rationnelle est tout simplement une chaîne de caractères qui décrit un motif. La notion de motif est couramment utilisée de nos jours. Par exemple, les motifs utilisés par un moteur de recherche pour trouver des pages web ou les motifs utilisés pour lister les fichiers dans un répertoire, e.g. C ou C. En Perl, les motifs d'expressions rationnelles sont utilisés pour chercher dans des chaînes de caractères, pour extraire certaines parties d'une chaîne et pour réaliser des opérations de recherche et de remplacement. Les expressions rationnelles ont la réputation d'être abstraite et difficile à comprendre. Les expressions rationnelles sont construites par assemblage de concepts simples tels que des conditions et des boucles qui ne sont pas plus compliqués à comprendre que les conditions C et les boucles C du langage Perl lui-même. En fait, le véritable enjeu dans l'apprentissage des expressions rationnelles réside dans la compréhension de la notation laconique utilisée pour exprimer ces concepts. Ce tutoriel aplanit la courbe d'apprentissage en présentant les concepts des expressions rationnelles, ainsi que leur notation, un par un et accompagnés d'exemples. La première partie de ce tutoriel commence par la simple recherche de mots pour aboutir aux concepts de base des expressions rationnelles. Si vous maîtriser cette première partie, vous aurez tous les outils nécessaires pour résoudre 98% de vos besoins. La seconde partie de ce tutoriel est destinée à ceux qui sont déjà à l'aise avec les bases et qui recherche des outils plus puissants. Elle explique les opérateurs les plus avancés des expressions rationnelles ainsi que les dernières innovations de la version 5.6.0. RemarqueE: pour gagner du temps, 'expression rationnelle' est parfois abrégée par regexp ou regex. Regexp est plus naturel (pour un anglophone) que regex mais est aussi plus dur à prononcer (toujours pour un anglophone). Dans la documentation Perl, on oscille entre regexp et regex ; en Perl, il y a toujours plusieurs façons d'abréger. Dans ce tutoriel en français, nous n'utiliserons que rarement regexp (N.d.t: même si l'abréviation française donne exprat !). =head1 Partie 1: les bases =head2 Reconnaissance d'un mot simple L'expression rationnelle la plus simple est un simple mot ou, plus généralement, une chaîne de caractères. Une expression rationnelle constituée d'un mot reconnaît toutes les chaînes qui contiennent ce motE: "Hello World" =~ /World/; # est reconnu Que signifie cette instruction Perl ? C<"Hello World"> est une simple chaîne de caractères entre guillemets. C est l'expression rationnelle et les C qui l'entourent (C) demandent à Perl d'en chercher une correspondance dans une chaîne. L'opérateur C<=~> associe la chaîne avec l'expression rationnelle de recherche de correspondance et produit une valeur vraie s'il y a correspondance ou faux sinon. Dans notre cas, C correspond au second mot dans C<"Hello World"> et donc l'expression est vraie. De telles expressions sont pratique dans des conditionsE: if ("Hello World" =~ /World/) { print "Il y a correspondance\n"; } else { print "Il n'y a pas correspondance\n"; } Il existe de nombreuses variations utiles sur ce thème. Le sens de la correspondance peut-être inversée en utilisant l'opérateur CE: if ("Hello World" !~ /World/) { print "Il n'y a pas correspondance\n"; } else { print "Il y a correspondance\n"; } La chaîne littérale dans l'expression rationnelle peut être remplacée par une variableE: $greeting = "World"; if ("Hello World" =~ /$greeting/) { print "Il y a correspondance\n"; } else { print "Il n'y a pas correspondance\n"; } Si vous recherchez dans la variable spéciale par défaut C<$_>, la partie C<$_ =~> peut être omiseE: $_ = "Hello World"; if (/World/) { print "Il y a correspondance\n"; } else { print "Il n'y a pas correspondance\n"; } Et finalement, les délimiteurs par défaut C pour une recherche de correspondance peuvent être remplacés par n'importe quels autres délimiteurs en les préfixant par un C<'m'>E: "Hello World" =~ m!World!; # correspond, délimiteur '!' "Hello World" =~ m{World}; # correspond, notez le couple '{}' "/usr/bin/perl" =~ m"/perl"; # correspond après '/usr/bin' # '/' devient un caractère comme un autre C, C et C représentent tous la même chose. Lorsque, par exemple, des guillemets (C<">) sont utilisés comme délimiteurs, la barre oblique C<'/'> devient un caractère ordinaire et peut être utilisée dans une expression rationnelle sans problème. Regardons maintenant comment différentes expressions rationnelles peuvent ou non trouver une correspondance dans C<"Hello World">E: "Hello World" =~ /world/; # ne correspond pas "Hello World" =~ /o W/; # correspond "Hello World" =~ /oW/; # ne correspond pas "Hello World" =~ /World /; # ne correspond pas La première expression rationnelle C ne correspond pas car les expressions rationnelles sont sensibles à la casse. La deuxième expression rationnelle trouve une correspondance car la sous-chaîne S> apparaît dans la chaîne S>. Le caractère espace ' ' est traité comme n'importe quel autre caractère dans une expression rationnelle et il est nécessaire ici pour trouver une correspondance. L'absence du caractère espace explique la non-reconnaissance de la troisième expression rationnelle. La quatrième expression rationnelle C<'World '> ne trouve pas de correspondance car il y a un espace à la fin de l'expression rationnelle et non à la fin de la chaîne. La leçon a tirer de ces exemples est qu'une expression rationnelle doit correspondre I à une partie de la chaîne pour être reconnue. Si une expression rationnelle peut être reconnue à plusieurs endroits dans une chaîne, Perl choisira toujours la correspondance au plus tôtE: "Hello World" =~ /o/; # correspond au 'o' dans 'Hello' "That hat is red" =~ /hat/; # correspond au 'hat' dans 'That' Il y a encore quelques points que vous devez savoir à propos de la reconnaissance de caractères. En premier lieu, tous les caractères ne peuvent pas être utilisés tels quels pour une correspondance. Quelques caractères, appelés I, sont réservés pour des notations d'expressions rationnelles. Les meta-caractères sontE: {}[]()^$.|*+?\ La signification de chacun d'eux sera expliquée plus loin dans ce tutoriel. Pour l'heure, il vous suffira de savoir qu'un meta-caractère sera recherché tel quel si vous le précédez d'un backslash (une barre oblique inversée)E: "2+2=4" =~ /2+2/; # pas de correspondance, + est un meta-caractère "2+2=4" =~ /2\+2/; # correspond, \+ est traité comme un + ordinaire "The interval is [0,1)." =~ /[0,1)./ # c'est une erreur de syntaxe ! "The interval is [0,1)." =~ /\[0,1\)\./ # correspond "#!/usr/bin/perl" =~ /#!\/usr\/bin\/perl/; # correspond Dans la dernière expression rationnelle, les slash C<'/'> sont aussi précédés d'un backslash parce que le slash est utilisé comme délimiteur de l'expression rationnelle. Par contre, cela peut aboutir au LTS (leaning toothpick syndrome) et il est donc souvent préférable de changer de délimiteur. "#!/usr/bin/perl" =~ m!#!/usr/bin/perl!; # plus facile à lire Le caractère backslash C<'\'> est lui-même un meta-caractère et doit donc être backslashé (précédé d'un backslash)E: 'C:\WIN32' =~ /C:\\WIN/; # correspond En plus des meta-caractères, il y a quelques caractères ASCII qui n'ont pas d'équivalent affichable et sont donc représentés par des I. Les plus courants sont C<\t> pour une tabulation, C<\n> pour un saut de ligne, C<\r> pour un retour chariot et C<\a> pour un beep. Si votre chaîne se présente plutôt comme une séquence d'octets quelconques, les séquences d'échappement en octal, telle que C<\033>, ou en hexadécimal, telle que C<\x1B> seront peut-être une représentation plus naturelle pour vos octets. Voici quelques exemples d'utilisation des séquences d'échappementE: "1000\t2000" =~ m(0\t2) # correspond "1000\n2000" =~ /0\n20/ # correspond "1000\t2000" =~ /\000\t2/ # ne correspond pas, "0" ne "\000" "cat" =~ /\143\x61\x74/ # correspond, une façon bizarre d'épeler 'cat' Si vous pratiquez déjà Perl, tout cela doit vous semblez familier. Des séquences similaires sont utilisées dans les chaînes entre guillemets. De fait, les expressions rationnelles en Perl sont traitées comme des chaînes entre guillemets. Cela signifie que l'on peut utiliser des variables dans les expressions rationnelles. Exactement comme pour les chaînes entre guillemets, chaque variable sera remplacée par sa valeur avant que l'expression rationnelle soit utilisée à la recherche de correspondances. DoncE: $foo = 'house'; 'housecat' =~ /$foo/; # correspond 'cathouse' =~ /cat$foo/; # correspond 'housecat' =~ /${foo}cat/; # correspond Jusqu'ici, ça va. Avec les connaissances qui précèdent vous pouvez déjà rechercher toutes les chaînes littérales imaginables. Voici une émulation I du programme Unix grepE: % cat > simple_grep #!/usr/bin/perl $regexp = shift; while (<>) { print if /$regexp/; } ^D % chmod +x simple_grep % simple_grep abba /usr/dict/words Babbage cabbage cabbages sabbath Sabbathize Sabbathizes sabbatical scabbard scabbards Ce programme est très simple à comprendre. C<#!/usr/bin/perl> est le moyen standard pour invoquer perl. S> mémorise le premier argument de la ligne de commande en tant qu'expression rationnelle à utiliser et laisse le reste des arguments pour qu'ils soient traiter comme des fichiers. S) >>> parcourt toutes les lignes de tous les fichiers. Pour chaque ligne, S> affiche la ligne si l'expression rationnelle trouve une correspondance dans la ligne. Dans cette ligne, C et C utilisent implicitement tous les deux la variable par défaut C<$_>. Dans toutes les expressions rationnelles précédentes, si l'expression rationnelle trouvait une correspondance n'importe où dans la chaîne, on considérait qu'elle correspondait. Parfois, par contre, nous aimerions spécifier I dans la chaîne où l'expression rationnelle doit trouver une correspondance. Pour cela, nous devons utiliser les meta-caractères d'ancrage C<^> et C<$>. L'ancre C<^> demande à correspondre au début de la chaîne et l'ancre C<$> demande à correspondre à la fin de la chaîne ou juste avant le passage à la ligne avant la fin de la chaîne. Voici comment elles sont utiliséesE: "housekeeper" =~ /keeper/; # correspond "housekeeper" =~ /^keeper/; # ne correspond pas "housekeeper" =~ /keeper$/; # correspond "housekeeper\n" =~ /keeper$/; # correspond La deuxième expression rationnelle ne correspond pas parce que C<^> contraint C à ne correspondre qu'au début de la chaîne. Or C<"housekeeper"> contient un "keeper" qui débute au milieu de la chaîne. La troisième expression rationnelle correspond puisque le C<$> contraint C à n'être reconnue qu'à la fin de la chaîne. Lorsque C<^> et C<$> sont utilisés ensemble, l'expression rationnelle doit être ancrée à la fois au début et à la fin de la chaîne, i.e., l'expression rationnelle correspond donc à la chaîne entière. ConsidéronsE: "keeper" =~ /^keep$/; # ne correspond pas "keeper" =~ /^keeper$/; # correspond "" =~ /^$/; # ^$ correspond à la chaîne vide La première expression rationnelle ne peut pas correspondre puisque la chaîne contient plus que C. Puisque la deuxième expression rationnelle est exactement la chaîne, elle correspond. L'utilisation combinée de C<^> et de C<$> force la chaîne entière à correspondre et vous donne donc un contrôle complet sur les chaînes qui correspondent et celles qui ne correspondent pas. Supposons que vous cherchez un individu nommé bertE: "dogbert" =~ /bert/; # correspond, mais ce n'est pas ce qu'on cherche "dilbert" =~ /^bert/; # ne correspond pas mais... "bertram" =~ /^bert/; # correspond, ce n'est donc pas encore bon "bertram" =~ /^bert$/; # ne correspond pas, ok "dilbert" =~ /^bert$/; # ne correspond pas, ok "bert" =~ /^bert$/; # correspond, parfait Bien sûr, dans le cas d'une chaîne littérale, n'importe qui utiliserait l'opérateur d'égalité des chaînes S> et cela serait beaucoup plus efficient. L'expression rationnelle C<^...$> devient beaucoup plus pratique lorsqu'on lui ajoute la puissance des outils d'expression rationnelle que nous verrons plus bas. =head2 Utilisation des classes de caractères Bien que certains puissent se satisfaire des expressions rationnelles précédentes qui ne reconnaissent que des chaînes littérales, nous n'avons qu'effleuré la technologie des expressions rationnelles. Dans cette section et les suivantes nous allons présenter les concepts d'expressions rationnelles (et les meta-caractères associés) qui permettent à une expression rationnelle de représenter non seulement une seule séquence de caractères mais aussi I de séquences. L'un de ces concepts et celui de I. Une classe de caractères autorise un ensemble de caractères, plutôt qu'un seul caractère, à correspondre en un point particulier de l'expression rationnelle. Les classes de caractères sont entourées de crochets C<[...]> avec l'ensemble de caractères placé à l'intérieur. Voici quelques exemplesE: /cat/; # reconnaît 'cat' /[bcr]at/; # reconnaît 'bat, 'cat', ou 'rat' /item[0123456789]/; # reconnaît 'item0' ou ... ou 'item9' "abc" =~ /[cab]/; # reconnaît le 'a' Dans la dernière instruction, bien que C<'c'> soit le premier caractère dans la classe, c'est le C<'a'> qui correspond car c'est le caractère le plus au début de la chaîne avec lequel l'expression rationnelle peut correspondre. /[oO][uU][iI]/; # reconnaît 'oui' sans tenir compte de la casse # 'oui', 'Oui, 'OUI', etc. Cette expression rationnelle réalise une tâche couranteE: une recherche de correspondance insensible à la casse. Perl fournit un moyen d'éviter tous ces crochets en ajoutant simplement un C<'i'> après l'expression rationnelle. Donc C peut être réécrit en C. Le C<'i'> signifie insensible à la casse et est un exemple de B de l'opération de reconnaissance. Nous rencontrerons d'autres modificateurs plus tard dans ce tutoriel. Nous avons vu dans la section précédente qu'il y avait des caractères ordinaires, qui se représentaient eux-mêmes et des caractères spéciaux qui devaient être backslashés pour se représenter. C'est la même chose dans les classes de caractères mais les ensembles de caractères ordinaires et spéciaux ne sont pas les mêmes. Les caractères spéciaux dans une classe de caractères sont C<-]\^$> (et les délimiteurs, quels qu'ils soient). C<]> est spécial car il indique la fin de la classe de caractères. C<$> est spécial car il indique une variable scalaire. C<\> est spécial car il est utilisé pour les séquences d'échappement comme précédemment. Voici comment les caractères spéciaux C<]$\> sont S /[\]c]def/; # reconnaît ']def' ou 'cdef' $x = 'bcr'; /[$x]at/; # reconnaît 'bat', 'cat', ou 'rat' /[\$x]at/; # reconnaît '$at' ou 'xat' /[\\$x]at/; # reconnaît '\at', 'bat, 'cat', ou 'rat' Les deux derniers exemples sont un peu complexes. Dans C<[\$x]>, le backslash protège le signe dollar et donc la classe de caractères contient deux membres : C<$> et C. Dans C<[\\$x]>, le backslash est lui-même protégé et donc C<$x> est traité comme une variable à laquelle on substitue sa valeur comme dans les chaînes entre guillemets. Le caractère spécial C<'-'> agit comme un opérateur d'intervalle dans une classe de caractères et donc un ensemble de caractères contigus peut être décrit comme un intervalle. Grâce aux intervalles, les classes peu maniables comme C<[0123456789]> et C<[abc...xyz]> deviennent très simplesE: C<[0-9]> et C<[a-z]>. Quelques exemplesE: /item[0-9]/; # reconnaît 'item0' ou ... ou 'item9' /[0-9bx-z]aa/; # reconnaît '0aa', ..., '9aa', # 'baa', 'xaa', 'yaa', or 'zaa' /[0-9a-fA-F]/; # reconnaît un chiffre hexadécimal /[0-9a-zA-Z_]/; # reconnaît un caractère d'un "mot", # comme ceux qui composent les noms en Perl Si C<'-'> est le premier ou le dernier des caractères dans une classe, il est traité comme un caractère ordinaire ; C<[-ab]>, C<[ab-]> et C<[a\-b]> sont équivalents. Le caractère spécial C<^> en première position d'une classe de caractères indique une I qui reconnaît n'importe quel caractère sauf ceux présents entre les crochets. Dans les deux cas, C<[...]> et C<[^...]>, il faut qu'un caractère soit reconnu sinon la reconnaissance échoue. DoncE: /[^a]at/; # ne reconnaît pas 'aat' ou 'at', mais reconnaît # tous les autres 'bat', 'cat, '0at', '%at', etc. /[^0-9]/; # reconnaît un caractère non numérique /[a^]at/; # reconnaît 'aat' or '^at'; ici '^' est ordinaire Même C<[0-9]> peut être ennuyeux à écrire plusieurs fois. Donc pour minimiser la frappe et rendre les expressions rationnelles plus lisibles, Perl propose plusieurs abréviations pour les classes les plus communesE: =over 4 =item * \d reconnaît un chiffre, non seulement [0-9] mais aussi les chiffres des langues non-romaines. =item * \s reconnaît un caractère d'espacement, l'ensemble [\ \t\r\n\f] et d'autres caractères. =item * \w reconnaît un caractère de mot (alphanumérique ou _), non seulement [0-9a-zA-Z_] mais aussi les caractères des langues non-romaines. =item * \D est la négation de \d; il représente n'importe quel caractère sauf un chiffre, autrement dit [^\d]. =item * \S est la négation de \s; il représente n'importe quel caractère qui ne soit pas d'espacement [^\s] =item * \W est la négation de \w; il représente n'importe quel caractère qui ne soit un pas un caractère de mot [^\w] =item * Le point '.' reconnaît n'importe quel caractère sauf "\n" (à moins que le modificateur C soit activé comme expliqué plus bas). =back Les abréviations C<\d\s\w\D\S\W> peuvent être utilisées à l'intérieur ou à l'extérieur des classes de caractères. Voici quelques exemplesE: /\d\d:\d\d:\d\d/; # reconnaît une heur au format hh:mm:ss /[\d\s]/; # reconnaît n'importe quel chiffre ou espacement /\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ère, suivis de 'rt' /end\./; # reconnaît 'end.' /end[.]/; # la même chose, reconnaît 'end.' Puisque un point est un meta-caractère, il doit être backslashé pour reconnaître un point ordinaire. Puisque, par exemple, C<\d> et C<\w> sont des ensembles de caractères, il est incorrect de penser que C<[^\d\w]> est équivalent à C<[\D\W]>; en fait C<[^\d\w]> est la même chose que C<[^\w]>, qui est la même chose que C<[\W]>. C'est l'application des lois ensemblistes de De Morgan. Une ancre pratique dans les expressions rationnelles de base est I C<\b>. Elle reconnaît la frontière entre un caractère mot et un caractère non-mot C<\w\W> ou C<\W\w>E: $x = "Housecat catenates house and cat"; $x =~ /cat/; # reconnaît cat dans 'housecat' $x =~ /\bcat/; # reconnaît cat dans 'catenates' $x =~ /cat\b/; # reconnaît cat dans 'housecat' $x =~ /\bcat\b/; # reconnaît 'cat' à la fin de la chaîne Notez que dans le dernier exemple, la fin de la chaîne est considérée comme une frontière de mot. Vous devez vous demander pourquoi C<'.'> reconnaît tous les caractères sauf C<"\n"> - pourquoi pas tous les caractères ? C'est parce que le plus souvent on effectue des mises en correspondance par ligne et que nous voulons ignorer le caractère de passage à la ligne. Par exemple, bien que la chaîne C<"\n"> représente une ligne, nous aimons la considérer comme vide. Par conséquentE: "" =~ /^$/; # est reconnue "\n" =~ /^$/; # est reconnue; $ s'ancre avant "\n" "" =~ /./; # n'est pas reconnue; nécessite un caractère "" =~ /^.$/; # n'est pas reconnue; nécessite un caractère "\n" =~ /^.$/; # n'est pas reconnue; nécessite un caractère autre que "\n" "a" =~ /^.$/; # est reconnue "a\n" =~ /^.$/; # est reconnue; $ s'ancre avant "\n" Ce comportement est pratique parce qu'habituellement nous voulons ignorer les passages à la ligne lorsque nous mettons en correspondance les caractères d'une ligne. Parfois, en revanche, nous voulons tenir compte des passages à la ligne. Nous pouvons aussi vouloir que les ancres C<^> et C<$> puissent s'ancrer au début et à la fin des lignes plutôt que simplement au début et à la fin de la chaîne. Perl nous permet de choisir entre ignorer ou tenir compte des passages à la ligne grâce aux modificateurs C et C. C et C signifient ligne simple et multi-ligne et ils déterminent si une chaîne doit être considérée comme une chaîne continue ou comme un ensemble de lignes. Les deux modificateurs agissent sur deux aspects de l'interprétation de l'expression rationnelleE: 1) comment la classe de caractères C<'.'> est définie et 2) où les ancres C<^> et C<$> peuvent s'ancrer. Voici les quatre combinaisonsE: =over 4 =item * pas de modificateurs (//)E: comportement par défaut. C<'.'> reconnaît n'importe quel caractère sauf C<"\n">. C<^> correspond uniquement au début de la chaîne et C<$> correspond uniquement à la fin de la chaîne ou avant le passage à la ligne avant la fin de la chaîne. =item * modificateur s (//s)E: traite la chaîne comme une seule longue ligne. C<'.'> reconnaît tous les caractères, même C<"\n">. C<^> correspond uniquement au début de la chaîne et C<$> correspond uniquement à la fin de la chaîne ou avant le passage à la ligne avant la fin de la chaîne. =item * modificateur m (//m)E: traite la chaîne comme un ensemble de lignes. C<'.'> reconnaît n'importe quel caractère sauf C<"\n">. C<^> et C<$> peuvent correspondre au début et à la fin de n'importe quelle ligne dans la chaîne. =item * les deux modificateurs s et m (//sm)E: traite la chaîne comme une seule longue ligne mais détecte les lignes multiples. C<'.'> reconnaît tous les caractères, même C<"\n">. C<^> et C<$> peuvent correspondre au début et à la fin de n'importe quelle ligne dans la chaîne. =back Voici des exemples d'utilisation de C et CE: $x = "There once was a girl\nWho programmed in Perl\n"; $x =~ /^Who/; # non reconnue, "Who" n'est pas en début de chaîne $x =~ /^Who/s; # non reconnue, "Who" n'est pas en début de chaîne $x =~ /^Who/m; # reconnue, "Who" au début de la seconde ligne $x =~ /^Who/sm; # reconnue, "Who" au début de la seconde ligne $x =~ /girl.Who/; # non reconnue, le "." ne reconnaît pas "\n" $x =~ /girl.Who/s; # reconnue, le "." reconnaît "\n" $x =~ /girl.Who/m; # non reconnue, le "." ne reconnaît pas "\n" $x =~ /girl.Who/sm; # reconnue, le "." reconnaît "\n" La plupart du temps, le comportement par défaut est ce que nous voulons mais C et C sont occasionnellement très utiles. Si C est utilisé, le début de la chaîne peut encore être reconnu par C<\A> et la fin de la chaîne peut aussi être reconnue soit par l'ancre C<\Z> (qui est reconnue à la fin de la chaîne ou juste avant le passage à la ligne, comme C<$>) soit par l'ancre C<\z> (qui n'est reconnue qu'à la fin de la chaîne)E: $x =~ /^Who/m; # reconnue, "Who" au début de la seconde ligne $x =~ /\AWho/m; # non reconnue, "Who" n'est pas au début de la chaîne $x =~ /girl$/m; # reconnue, "girl" à la fin de la première ligne $x =~ /girl\Z/m; # non reconnue, "girl" n'est pas à la fin de la chaîne $x =~ /Perl\Z/m; # reconnue, "Perl" est juste avant le passage à la ligne # de la fin de chaîne $x =~ /Perl\z/m; # non reconnue, "Perl" n'est pas à la fin de la chaîne Nous savons maintenant comment créer des choix à travers des classes de caractères dans les expressions rationnelles. Qu'en est-il de choix entre des mots ou des chaînes de caractères ? Ce sont ces choix qui sont décrits dans la section suivante. =head2 Reconnaître ceci ou cela Parfois nous aimerions que notre expression rationnelle soit capable de reconnaître différents mots ou suites de caractères. Ceci s'effectue en utilisant le meta-caractère d'I C<|>. Pour reconnaître C ou C, nous formons l'expression rationnelle C. Comme précédemment, Perl essaie de reconnaître l'expression rationnelle le plus tôt possible dans la chaîne. À chaque position, Perl essaie en premier de reconnaître la première branche de l'alternative, C. Si C n'est pas reconnu, Perl essaie ensuite la branche suivante, C. Si C n'est pas reconnu non plus alors la mise en correspondance échoue et Perl se déplace à la position suivante dans la chaîne. Quelques exemplesE: "cats and dogs" =~ /cat|dog|bird/; # reconnaît "cat" "cats and dogs" =~ /dog|cat|bird/; # reconnaît "cat" Même si C est la première branche de l'alternative dans le second exemple, C est reconnu plus tôt dans la chaîne. "cats" =~ /c|ca|cat|cats/; # reconnaît "c" "cats" =~ /cats|cat|ca|c/; # reconnaît "cats" Ici, toutes les branches peuvent correspondre à la première position dont la première branche reconnue est celle qui est retenue. Si certaines branches de l'alternative sont des troncatures des autres, placez les plus longues en premier pour leur donner une chance d'être reconnues. "cab" =~ /a|b|c/ # reconnaît "c" # /a|b|c/ == /[abc]/ Ce dernier exemple montre que les classes de caractères sont comme des alternatives entre caractères. À une position donnée, la première branche qui permet une mise en correspondance de l'expression rationnelle est celle qui sera reconnue. =head2 Regroupement et reconnaissance hiérarchique Les alternatives permettent à une expression rationnelle de choisir entre différentes branches mais elles restent non satisfaisantes en elles-mêmes. Pour la simple raison que l'alternative est une expression rationnelle complète alors que parfois nous aimerions que ce ne soit qu'une partie de l'expression rationnelle. Par exemple, supposons que nous voulions reconnaître housecat ou housekeeper. L'expression rationnelle C fonctionne mais est inefficace puisque nous avons dû taper C deux fois. Il serait pratique d'avoir une partie de l'expression rationnelle qui soit constante, comme C, et une partie qui soit une alternative, comme C. Les meta-caractères de I C<()> résolvent ce problème. Le regroupement permet à une partie d'une expression rationnelle d'être traiter comme une seule entité. Une partie d'une expression rationnelle est regroupée en la plaçant entre des parenthèses. Donc nous pouvons résoudre le problème C en formant l'expression rationnelle C. L'expression rationnelle C signifie cherche C suivi soit par C soit par C. Quelques exemplesE: /(a|b)b/; # reconnaît 'ab' ou 'bb' /(ac|b)b/; # reconnaît 'acb' ou 'bb' /(^a|b)c/; # reconnaît 'ac' au début de la chaîne ou 'bc' n'importe où /(a|[bc])d/; # reconnaît 'ad', 'bd', ou 'cd' /house(cat|)/; # reconnaît soit 'housecat' soit 'house' /house(cat(s|)|)/; # reconnaît soit 'housecats' soit 'housecat' soit # 'house'. Les groupes peuvent être imbriqués. /(19|20|)\d\d/; # reconnaît les années 19xx, 20xx, ou xx "20" =~ /(19|20|)\d\d/; # reconnaît la branche vide '()\d\d', # puisque '20\d\d' ne peut pas correspondre Les alternatives se comportent de la même manière, qu'elles soient dans ou hors d'un groupeE: à une position donnée, c'est la branche la plus à gauche qui est choisie tant qu'elle permet la reconnaissance de l'expression rationnelle. Donc dans notre dernier exemple, à la première position de la chaîne, C<"20"> est reconnu par la deuxième branche de l'alternative mais il ne reste rien pour être reconnu par les deux chiffres suivants C<\d\d>. Alors Perl essaie la branche suivante qui est la branche vide et ça marche puisque C<"20"> est composé de deux chiffres. Le processus d'essai d'une branche pour voir si elle convient puis de retour en arrière pour en essayer une autre est appelé I (I en anglais). Les termes 'retour arrière' viennent de l'idée que la reconnaissance d'une expression rationnelle est comme une marche en forêt. La reconnaissance de l'expression rationnelle est comme l'arrivée à destination. Il y a plusieurs points de départ possibles, un pour chaque position dans la chaîne et chacun d'eux est essayé dans l'ordre, de gauche à droite. À partir de chaque point de départ, il y a plusieurs chemins dont certains sont des impasses et d'autres vous amènent à destination. Lorsque vous marchez sur un chemin et qu'il aboutit à une impasse, vous devez retourner en arrière pour essayer un autre chemin. Vous êtes persévérant et vous ne vous déclarez vaincu que lorsque vous avez essayé tous les chemins à partir de tous les points de départ sans aboutir à destination. Pour être plus concret, voici une analyse pas à pas de ce que Perl fait lorsqu'il essaye de reconnaître l'expression rationnelleE: "abcde" =~ /(abd|abc)(df|d|de)/; =over 4 =item 1 On démarre avec la première lettre de la chaîne 'a'. =item 2 On essaie la première branche de la première alternative, le groupe 'abd'. =item 3 On reconnaît un 'a' suivi d'un 'b'. Jusqu'ici, ça va. =item 4 'd' dans l'expression rationnelle ne correspond pas au 'c' dans la chaîne - une impasse. On effectue alors un retour arrière de deux caractères et on essaie la seconde branche de la première alternative, le groupe 'abc'. =item 5 On reconnaît un 'a' suivi d'un 'b' suivi d'un 'c'. Nous avons donc satisfait le premier regroupement. On positionne $1 à 'abc'. =item 6 On continue avec la seconde alternative et on choisit la première branche 'df'. =item 7 On reconnaît le 'd'. =item 8 'f' dans l'expression rationnelle ne correspond pas au 'e' dans la chaîne; c'est donc une impasse. Retour arrière d'un caractère et choix de la seconde branche de la seconde alternative 'd'. =item 9 'd' est reconnu. Le second regroupement est satisfait et on positionne $2 à 'd'. =item 10 Nous sommes à la fin de l'expression rationnelle. Nous sommes donc arrivés à destination ! Nous avons reconnu 'abcd' dans la chaîne "abcde". =back Il y a deux choses à dire sur cette analyse. Tout d'abord, la troisième alternative ('de') dans le second regroupement permet aussi une reconnaissance, mais nous nous sommes arrêtés avant d'y arriver - à une position donnée, l'alternative la plus à gauche l'emporte. Ensuite, nous avons obtenu une reconnaissance au premier caractère ('a') de la chaîne. Si la reconnaissance n'avait pas été possible à cette première position, Perl se serait déplacer à la deuxième position ('b') pour essayer à nouveau une reconnaissance complète. C'est uniquement lorsque tous les chemins et toutes les positions ont été essayés sans succès que Perl s'arrête et déclare fausse l'assertion S>. Même avec tout ce boulot, les expressions rationnelles restent remarquablement rapides. Pour améliorer cela, Perl compile les expressions rationnelles en une séquence compacte d'opérations qui peut parfois tenir dans le cache du processeur. Lorsque le code est exécuté, ces opérations peuvent alors tourner à plein régime et chercher très rapidement. =head2 Extraire ce qui est reconnu Les meta-caractères de regroupement C<()> servent aussi à une fonction totalement S ils permettent l'extraction des parties d'une chaîne qui ont trouvé une correspondance. C'est très pratique pour trouver ce qui a été reconnu et pour le traitement de texte en général. Pour chaque groupe, la partie qui a été reconnue est stockée dans les variables spéciales C<$1>, C<$2>, etc. Elles peuvent être utilisées comme des variables ordinairesE: # extraction des heures, minutes et secondes if ($time =~ /(\d\d):(\d\d):(\d\d)/) { # reconnaît le format hh:mm:ss $heures = $1; $minutes = $2; $secondes = $3; } Pour l'instant, nous savons que, dans un contexte scalaire, S> retourne une valeur vraie ou fausse. Dans un contexte de liste, en revanche, il retourne la liste de valeurs reconnues C<($1,$2,$3)>. Nous pouvons donc écrire du code plus compactE: # extraction des heures, minutes et secondes ($heures, $minutes, $secondes) = ($time =~ /(\d\d):(\d\d):(\d\d)/); Si les regroupements sont imbriqués dans l'expression rationnelle, C<$1> recevra le groupe dont la parenthèse ouvrante est la plus à gauche, C<$2> recevra le groupe dont la parenthèse ouvrante est la seconde la plus à gauche, etc. Par exemple, voici une expression rationnelle avec regroupementsE: /(ab(cd|ef)((gi)|j))/; 1 2 34 Si cette expression rationnelle est reconnue, C<$1> contiendra une chaîne commençant par C<'ab'>, C<$2> contiendra soit 'cd' soit 'ef', C<$3> sera soit C<'gi'> soit C<'j'> et finalement C<$4> sera C<'gi'>, exactement comme C<$3>, ou restera non défini. De manière pratique, perl assigne à C<$+> la chaîne associée au plus haut numéro des C<$1>, C<$2>... qui a été affectée (et C<$^N> sera la valeur des variables C<$1>, C<$2>... la plus récemment affectée, c'est-à-dire celle associée à la parenthèse fermante la plus à droite utilisée lors de la reconnaissance). =head2 Les références arrières Associées avec les variables C<$1>, C<$2>, ..., on trouve les IE: C<\1>, C<\2>, ... Les références arrières sont simplement des variables de reconnaissance qui peuvent être utilisées I<à l'intérieur> de l'expression rationnelle. C'est une fonctionnalité vraiment sympathique - ce qui est reconnu plus tard dans une expression rationnelle dépend de ce qui a été reconnu plus tôt dans l'expression rationnelle. Supposons que nous voulons chercher les mots doublés dans un texte comme 'the the'. L'expression rationnelle suivante trouve tous les mots de trois lettres doublés avec un espace entre les deuxE: /\b(\w\w\w)\s\1\b/; Le regroupement affecte une valeur à \1 et donc la même séquence de 3 lettres est utilisée pour les deux parties. Voici quelques mots avec des parties répétéesE: % simple_grep '^(\w\w\w\w|\w\w\w|\w\w|\w)\1$' /usr/dict/words beriberi booboo coco mama murmur papa L'expression rationnelle commence par un regroupement qui considère d'abord les combinaisons de 4 lettres, puis de 3 lettres, etc., et utilise ensuite C<\1> pour chercher une répétition. Bien que C<$1> et C<\1> représente la même chose, faites attention à n'utiliser les variables C<$1>, C<$2>, ... qu'I<à l'extérieur> d'une expression rationnelle et à n'utiliser les références arrières C<\1>, C<\2>, ... qu'I<à l'intérieur> d'une expression rationnelle. Ne pas y prêter attention peut amener à des résultats surprenants et indéfinis. =head2 Références arrières relatives Le comptage des parenthèses ouvrantes pour trouver le numéro correct attribué à une référence arrière est sujet à erreur en présence de plusieurs regroupements. Une technique plus pratique est proposé depuis Perl S<5.10 :> les références arrières relatives. Pour se référer au dernier regroupement qui précède on peut utiliser C<\g{-1}>, l'avant-dernier regroupement est accessible via C<\g{-2}> et ainsi de suite. En plus d'améliorer lisibilité et la maintenabilité, un autre avantage des référer arrières relatives est illustré pour l'exemple suivant où un simple motif est utilisé pour reconnaître quelques chaînes S $a99a = '([a-z])(\d)\2\1'; # reconnaît a11a, g22g, x33x, etc. Maintenant que ce motif est stocké dans une chaîne, nous pouvons être tenté de l'utiliser à l'intérieur d'un autre S $ligne = "code=e99e"; if ($ligne =~ /^(\w+)=$a99a$/){ # comportement inattendu ! print "$1 est valide\n"; } else { print "ligne invalide : '$ligne'\n"; } Mais ça ne marche pas -- tout du moins pas comme nous l'attendions. Ce n'est qu'en insérant par interpolation la valeur de C<$a99a> et en regardant l'expression rationnelle obtenue qu'on constate que les références arrières sont erronées -- le regroupement C<(\w+)> a capté le numéro 1 et décalé d'un cran le regroupement présent dans C<$a99a>. Cela peut être évité en utilisant les références arrières S $a99a = '([a-z])(\d)\g{-1}\g{-2}'; # Ok pour être interpolé =head2 Références arrières nommées Perl 5.10 propose aussi les regroupements nommés et les références arrières nommées. Pour rattacher un nom à un groupe, vous pouvez écrire soit C<< (?...) >> soit C<< (?'nom'...) >>. La référence arrière correspondante s'écrit alors C<\g{nom}>. Il est possible d'attacher le même nom à plusieurs regroupements mais seul le plus à gauche d'entre eux peut-être référencé. En dehors des expressions rationnelles, on peut accéder aux regroupements nommés via la table de hachage C<%+>. Supposons que vous cherchez à reconnaître des dates fournies dans l'un des trois formats suivants : yyyy-mm-dd, mm/dd/yyyy ou dd.mm.yyyy. Vous pouvez écrire trois motifs qui tous utiliseront les noms 'd', 'm' et 'y' associés aux regroupements reconnaisant chacun une composante de la date. L'opération de reconnaissance suivante combinent ces trois motifs dans une S $fmt1 = '(?\d\d\d\d)-(?\d\d)-(?\d\d)'; $fmt2 = '(?\d\d)/(?\d\d)/(?\d\d\d\d)'; $fmt3 = '(?\d\d)\.(?\d\d)\.(?\d\d\d\d)'; for my $d qw( 2006-10-21 15.01.2007 10/31/2005 ){ if ( $d =~ m{$fmt1|$fmt2|$fmt3} ){ print "day=$+{d} month=$+{m} year=$+{y}\n"; } } Si l'une des branches de l'alternative est reconnues alors la table de hachage C<%+> contiendra les trois paires clé/valeur recherchées. =head2 Numérotation des groupes dans une alternative Uen autre technique de numérotation (existant elle aussi depuis Perl 5.10) gère le problème du référencement des groupes dans une alternative. Voici une expression rationnelle reconnaissant une heure du jour, à la manière civile ou S if ( $time =~ /(\d\d|\d):(\d\d)|(\d\d)(\d\d)/ ){ # traitement des heures et minutes } Le traitement du résultat nécessite une instruction de test supplémentaire pour déterminer si c'est C<$1> et C<$2> qui contiennent les bonnes valeurs ou au contraire C<$3> et C<$4>. Cela serait plus facile si les deux branches de l'alternative utilisaient toutes les deux les numéros de groupes 1 et 2. C'est exactement ce à quoi sert la construction C<(?|...)> autour d'une alternative. Voici une version plus complète de l'expression rationnelle S if ( $time =~ /(?|(\d\d|\d):(\d\d)|(\d\d)(\d\d))\s+([A-Z][A-Z][A-Z])/ ){ print "hour=$1 minute=$2 zone=$3\n"; } À l'intérieur de l'alternative, pour chaque branche, la numérotation des groupes commencent à la même position. Après l'alternative, la numérotation continue au numéro juste après le numéro le plus élevé atteint par les branches de l'alternative. =head2 Information de position En plus de ce qui a été reconnu, depuis la version 5.6.0, Perl fournit aussi la position de ce qui a été reconnu grâce aux tableaux C<@-> et C<@+>. C<$-[0]> est la position du début de l'ensemble de la reconnaissance et C<$+[0]> est la position de la fin. De manière similaire, C<$-[n]> est la position du début du groupe C<$n> et C<$+[n]> est la position de sa fin. Si C<$n> est indéfini alors C<$-[n]> et C<$+[n]> le sont aussi. Donc le codeE: $x = "Mmm...donut, thought Homer"; $x =~ /^(Mmm|Yech)\.\.\.(donut|peas)/; # reconnu foreach $expr (1..$#-) { print "Match $expr: '${$expr}' at position ($-[$expr],$+[$expr])\n"; } afficheE: Match 1: 'Mmm' at position (0,3) Match 2: 'donut' at position (6,11) Même s'il n'y a aucun regroupement dans l'expression rationnelle, il est encore possible de retrouver exactement ce qui a été reconnu dans la chaîne. Si vous les utilisez, Perl donnera pour valeur à C<$`> la partie de la chaîne qui est avant ce qui a été reconnu, à C<$&> la partie de la chaîne qui a été reconnue et à C<$'> la partie de la chaîne qui est après ce qui a été reconnu. Un exempleE: $x = "the cat caught the mouse"; $x =~ /cat/; # $` = 'the ', $& = 'cat', $' = ' caught the mouse' $x =~ /the/; # $` = '', $& = 'the', $' = ' cat caught the mouse' Lors de la seconde mise en correspondance, C<$`> est égal à C<''> parce que l'expression rationnelle est reconnue au premier caractère dans la chaîne et elle ne voit donc jamais le second 'the'. Il est important de noter que l'utilisation de C<$`> et C<$'> ralentissent un peu le processus de reconnaissance des expressions rationnelles et que l'utilisation de C<$&> le ralentit aussi, mais un peu moins parce que si ces variables sont utilisées une fois pour une expression rationnelle, elles sont alors calculées pour I les expressions rationnelles du programme. Donc si les performances font parties des buts dans votre projet, elles doivent être évitées. Préférez plutôt l'utilisation de C<@-> et de C<@+>E: $` est la même chose que substr( $x, 0, $-[0] ) $& est la même chose que substr( $x, $-[0], $+[0]-$-[0] ) $' est la même chose que substr( $x, $+[0] ) =head2 Regroupements sans mémorisation Un regroupement est indispensable pour construire une alternative mais sa mémorisation n'est pas toujours nécessaire. Une mémorisation inutile peut perturber tant à l'intérieur qu'à l'extérieur de l'expression rationnelle. C'est pour éviter cela qu'on été créé les regroupements sans mémorisation, notés C<(?:regexp)>, qui permettent toujours de considérer leur contenu comme une seule chose mais sans déclencher sa mémorisation. Les deux types de regroupements peuvent coexister dans une même expression rationnelle. Puisqu'il n'y a pas extraction de l'information, les regroupements sans mémorisation sont plus rapides que ceux avec. Cela permet aussi de choisir précisément les parties de l'expression rationnelle que l'on veut extraire via les variables de S # reconnaît un nombre, $1-$4 sont affectés, # mais nous ne voulons que $1 /([+-]?\ *(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?)/; # reconnaît un nombre plus rapidement, seul $1 est affecté /([+-]?\ *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)/; # reconnaît un nombre, avec $1 = le nombre complet, $2 = l'exposant /([+-]?\ *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE]([+-]?\d+))?)/; Les regroupements sans mémorisation sont aussi utiles pour éviter les nuisances que la mémorisation peut avoir sur une opération de découpage (via split) où un regroupement est nécessaire. $x = '12aba34ba5'; @num = split /(a|b)+/, $x; # @num = ('12','a','34','b','5') @num = split /(?:a|b)+/, $x; # @num = ('12','34','5') =head2 Reconnaissances répétées Quelques exemples de la section précédente présentent un défaut. Nous ne reconnaissons que les mots de 3 lettres ou des syllabes de 4 lettres ou moins. Nous aimerions pouvoir reconnaître des mots ou des syllabes de n'importe quelle longueur sans écrire des alternatives horribles comme C<\w\w\w\w|\w\w\w|\w\w|\w>. C'est exactement pour répondre à ce besoin que les meta-caractères I C, C<*>, C<+> et C<{}> ont été créés. Ils nous permettent de spécifier le nombre de répétitions d'une portion d'une expression rationnelle que nous considérons comme acceptable. Les quantificateurs se placent juste après le caractère, la classe de caractères ou le regroupement dont ils précisent le nombre de répétitions. Ils ont la signification suivanteE: =over 4 =item * C S reconnaît 'a' 1 ou 0 fois =item * C S reconnaît 'a' 0 ou plusieurs fois =item * C S reconnaît 'a' 1 ou plusieurs fois =item * C S reconnaît au moins C 'a', mais pas plus que C 'a'. =item * C S reconnaît au moins C 'a' ou plus =item * C S reconnaît exactement C 'a' =back Voici quelques exemplesE: /[a-z]+\s+\d*/; # reconnaît un mot en minuscules, au moins un espace et # n'importe quel nombre de chiffres /(\w+)\s+\1/; # reconnaît la répétition d'un mot de n'importe # quelle longueur /y(es)?/i; # reconnaît 'y', 'Y', ou un 'yes' insensible à la cass $year =~ /\d{2,4}/; # s'assure que l'année est au moins sur 2 chiffres # et pas sur plus de 4 chiffres $year =~ /\d{4}|\d{2}/; # meilleur reconnaissance ; supprime le cas # d'une année sur 3 chiffres $year =~ /\d{2}(\d{2})?/; # la même chose écrite différement ; de plus, # produit $1 ce que ne faisaient pas les # exemples précédents % simple_grep '^(\w+)\1$' /usr/dict/words # c'est simple... non ? beriberi booboo coco mama murmur papa Quel que soit le quantificateur, Perl essaye de reconnaître la chaîne la plus longue possible tant qu'elle peut être mise en correspondance avec l'expression rationnelle. Donc dans C, Perl essayera d'abord de reconnaître l'expression rationnelle avec un C présent ; en cas d'échec, Perl réessayera sans le C. Avec le quantificateur C<*>, nous obtenonsE: $x = "the cat in the hat"; $x =~ /^(.*)(cat)(.*)$/; # est reconnue avec # $1 = 'the ' # $2 = 'cat' # $3 = ' in the hat' C'est exactement ce que nous attendions, la mise en correspondance trouve le seul C présent dans la chaîne et se cale dessus. Considérons maintenant cette expression rationnelleE: $x =~ /^(.*)(at)(.*)$/; # est reconnue avec # $1 = 'the cat in the h' # $2 = 'at' # $3 = '' (reconnaissance de la chaîne vide) On aurait pu croire que Perl trouverait le C dans C et se serait arrêté là mais cela n'aurait pas donné la chaîne la plus longue possible pour le premier quantificateur C<.*>. En fait, le premier quantificateur C<.*> consomme la plus grande partie possible de la chaîne tout en laissant à l'expression rationnelle la possibilité d'être reconnue. Dans cet exemple, cela signifie que la séquence C est mise en correspondance avec le dernier C de la chaîne. L'autre principe important illustré ici est que lorsque qu'il y a plusieurs éléments dans une expression rationnelle, c'est le quantificateur le plus à gauche, s'il existe, qui est servi en premier et qui laisse le minimum au reste de l'expression rationnelle. Donc dans notre exemple, c'est le premier quantificateur C<.*> qui consomme la plus grande partie de la chaîne alors que le second quantificateur C<.*> obtient la chaîne vide. Les quantificateurs qui consomment autant que possible sont qualifiés de I ou de I. Lorsque une expression rationnelle peut être mise en correspondance avec une chaîne de différentes façons, nous pouvons utiliser les principes suivants pour prédire la manière dont elle sera reconnueE: =over 4 =item * Principe S<0 :> Tout d'abord, une expression rationnelle sera toujours reconnue à la position la plus à gauche possible dans la chaîne. =item * Principe S<1 >: Dans un choix C, c'est la branche la plus à gauche permettant une reconnaissance de l'ensemble de l'expression rationnelle qui sera choisie en premier. =item * Principe S<2 :> Les quantificateurs gourmands comme C, C<*>, C<+> et C<{n,m}> consomme la plus grande partie possible de la chaîne tant qu'ils permettent encore de reconnaître l'ensemble de l'expression rationnelle. =item * Principe S<3 :> s'il existe plusieurs éléments dans une expression rationnelle, le quantificateur gourmand le plus à gauche, s'il existe, sera mis en correspondance avec la partie de la chaîne la plus longue possible tout en gardant possible la reconnaissance de toute l'expression rationnelle. Le quantificateur gourmand suivant, s'il existe, sera mis en correspondance avec la partie la plus longue possible de ce qui reste de la chaîne tout en gardant possible la reconnaissance de toute l'expression rationnelle. Et ainsi de suite, jusqu'à la reconnaissance complète de l'expression rationnelle. =back Comme vu précédemment, le Principe 0 est prioritaire sur tous les autres - l'expression rationnelle est reconnue le plus tôt possible en utilisant les autres principes pour déterminer comment l'expression rationnelle est reconnue à cette position la plus à gauche. Voici des exemples qui montrent l'application de ces principesE: $x = "The programming republic of Perl"; $x =~ /^(.+)(e|r)(.*)$/; # est reconnue avec # $1 = 'The programming republic of Pe' # $2 = 'r' # $3 = 'l' L'expression rationnelle est reconnue à la position la plus tôt, C<'T'>. On pourrait penser que le C le plus à gauche de l'alternative devrait être reconnu mais le C produit une chaîne plus longue pour le premier quantificateur. $x =~ /(m{1,2})(.*)$/; # est reconnue avec # $1 = 'mm' # $2 = 'ing republic of Perl' Ici, la mise en correspondance au plus tôt se fait au premier C<'m'> de C. C est le premier quantificateur et donc il cherche la correspondance maximale C. $x =~ /.*(m{1,2})(.*)$/; # est reconnue avec # $1 = 'm' # $2 = 'ing republic of Perl' Ici, l'expression rationnelle est mise en correspondance dès le début de la chaîne. Le premier quantificateur C<.*> consomme le plus de caractères possibles en ne laissant qu'un seul C<'m'> pour le second quantificateur C. $x =~ /(.?)(m{1,2})(.*)$/; # est reconnue avec # $1 = 'a' # $2 = 'mm' # $3 = 'ing republic of Perl' Ici, C<.?> consomme le maximum de caractères (un caractère) à la position la plus à gauche possible dans la chaîne, le C<'a'> dans C, en laissant l'opportunité à C de correspondre aux deux C. FinalementE: "aXXXb" =~ /(X*)/; # est reconnue avec $1 = '' parce qu'il peut reconnaître zéro C<'X'> au tout début de la chaîne. Si vous voulez réellement reconnaître au moins un C<'X'>, utilisez C au lieu de C. Parfois la gourmandise n'est pas une bonne chose. Dans ce cas, nous aimerions des quantificateurs qui reconnaissent une partie I de la chaîne plutôt que I. Pour cela, Larry Wall a créé les quantificateurs I ou quantificateurs IE: C,C<*?>, C<+?> et C<{}?>. Ce sont les quantificateurs habituels auxquels on ajoute un C. Ils ont la sémantique suivanteE: =over 4 =item * C S reconnaît un 'a' 0 ou 1 fois. Essaie 0 d'abord puis 1 ensuite. =item * C S reconnaît zéro 'a' ou plus mais un minimum de fois. =item * C S reconnaît un 'a' ou plus mais un minimum de fois. =item * C S reconnaît entre C et C 'a' mais un minimum de fois. =item * C S reconnaît au moins C 'a' mais un minimum de fois. =item * C = reconnaît exactement C 'a'. C'est équivalent à C. Ce n'est donc que pour rendre la notation cohérente. =back Reprenons les exemples précédents mais avec des quantificateurs minimauxE: $x = "The programming republic of Perl"; $x =~ /^(.+?)(e|r)(.*)$/; # est reconnue avec # $1 = 'Th' # $2 = 'e' # $3 = ' programming republic of Perl' La chaîne minimale permettant à la fois de reconnaître le début de chaîne C<^> et l'alternative est C avec l'alternative C qui reconnaît C. Le second quantificateur est libre de consommer tout le reste de la chaîne. $x =~ /(m{1,2}?)(.*?)$/; # est reconnue avec # $1 = 'm' # $2 = 'ming republic of Perl' La première position à partir de laquelle cette expression rationnelle peut être reconnue et le premier C<'m'> de C. À cette position, le sobre C reconnaît juste un C<'m'>. Ensuite, bien que le second quantificateur C<.*?> préfère reconnaître un minimum de caractères, il est contraint par l'ancre de fin de chaîne C<$> à reconnaître tout le reste de la chaîne. $x =~ /(.*?)(m{1,2}?)(.*)$/; # est reconnue avec # $1 = 'The progra' # $2 = 'm' # $3 = 'ming republic of Perl' Dans cette expression rationnelle, vous espériez peut-être que le premier quantificateur minimal C<.*?> soit mis en correspondance avec la chaîne vide puisqu'il n'est pas contraint à s'ancrer en début de chaîne par C<^>. Mais ici le principe 0 s'applique. Puisqu'il est possible de reconnaître l'ensemble de l'expression rationnelle en s'ancrant au début de la chaîne, ce sera cet ancrage qui sera choisi. Donc le premier quantificateur doit reconnaître tout jusqu'au premier C et le troisième quantificateur reconnaît le reste de la chaîne. $x =~ /(.??)(m{1,2})(.*)$/; # est reconnue avec # $1 = 'a' # $2 = 'mm' # $3 = 'ing republic of Perl' Comme dans l'expression rationnelle précédente, le premier quantificateur peut correspondre au plus tôt sur le C<'a'>, c'est donc ce qui est retenu. Le second quantificateur est gourmand donc il reconnaît C et le troisième reconnaît le reste de la chaîne. Nous pouvons modifier le principe 3 précédent pour prendre en compte les quantificateurs sobresE: =over 4 =item * Principe 3: s'il existe plusieurs éléments dans une expression rationnelle, le quantificateur gourmand (ou sobre) le plus à gauche, s'il existe, sera mis en correspondance avec la partie de la chaîne la plus longue (ou la plus courte) possible tout en gardant possible la reconnaissance de toute l'expression rationnelle. Le quantificateur gourmand (ou sobre) suivant, s'il existe, sera mis en correspondance avec la partie la plus longue (ou la plus courte) possible de ce qui reste de la chaîne tout en gardant possible la reconnaissance de toute l'expression rationnelle. Et ainsi de suite, jusqu'à la reconnaissance complète de l'expression rationnelle. =back Comme avec les alternatives, les quantificateurs sont susceptibles de déclencher des retours arrière. Voici l'analyse pas à pas d'un exempleE: $x = "the cat in the hat"; $x =~ /^(.*)(at)(.*)$/; # est reconnue avec # $1 = 'the cat in the h' # $2 = 'at' # $3 = '' (reconnaissance de la chaîne vide) =over 4 =item 1 On démarre avec la première lettre de la chaîne 't'. =item 2 Le premier quantificateur '.*' commence par reconnaître l'ensemble de la chaîne 'the cat in the hat'. =item 3 Le 'a' de l'élément 'at' ne peut pas être mis en correspondance avec la fin de la chaîne. Retour arrière d'un caractère. =item 4 Le 'a' de l'élément 'at' ne peut pas être mis en correspondance avec la dernière lettre de la chaîne 't'. Donc à nouveau un retour arrière d'un caractère. =item 5 Maintenant nous pouvons reconnaître le 'a' et le 't'. =item 6 On continue avec le troisième élément '.*'. Puisque nous sommes à la fin de la chaîne, '.*' ne peut donc reconnaître que 0 caractère. On lui affecte donc la chaîne vide. =item 7 C'est fini. =back La plupart du temps, tous ces aller-retours ont lieu très rapidement et la recherche est rapide. Par contre, il existe quelques expressions rationnelles pathologiques dont le temps d'exécution augmente exponentiellement avec la taille de la chaîne. Une structure typique est de la formeE: /(a|b+)*/; Le problème est l'imbrication de deux quantificateurs indéterminés. Il y a de nombreuses manières de partitionner une chaîne de longueur n entre le C<+> et le C<*>E: une répétition avec C de longueur n, deux répétitions avec le premier C de longueur k et le second de longueur n-k, m répétitions dont la somme des longueurs est égale a n, etc. En fait, le nombre de manières différentes de partitionner une chaîne est exponentielle en fonction de sa longueur. Une expression rationnelle peut être chanceuse et être reconnue très tôt mais s'il n'y a pas de reconnaissance possible, Perl essayera I les possibilités avant de conclure à l'échec. Donc, soyez prudents lorsque vous imbriquez des C<*>, des C<{n,m}> et/ou des C<+>. Le livre I par Jeffrey Friedl donne de très bonnes explications sur ce problème en particulier et sur tous les autres problèmes de performances. =head2 Quantificateurs possessifs Effectuer des retours arrières pendant la recherche acharnée d'une reconnaissance peut être une perte de temps, particulièrement si cette recherche est sûre d'échouer. Considérons cette simple S /^\w+\s+\w+$/; # un mot, un espace, un mot À chaque fois qu'elle est appliquée à une chaîne qui ne correspond pas à ce qui est attendu telle S> ou S>, le moteur de regexp effectue des retours arrières, approximativement pour chaque caractère de la chaîne. Or nous savons qu'il n'y a pas d'autre choix que de consommer I les caractères mot initiaux pour pouvoir reconnaître la première répétition, que I les espaces qui suivent doivent être mangés par la seconde répétition et il en est de même pour le deuxième mot. Avec l'introduction des I en Perl 5.10, nous avons moyen d'empêcher les retours arrières du moteur de regexp, en ajoutant un C<+> après un quantificateur usuel. Cela les rend à la fois gourmands et S une fois qu'ils sont reconnus, ils ne rendent plus aucun caractère pour essayer une autre solution. Ils ont la signification S =over 4 =item * C S reconnaît au moins C fois, mais pas plus que C fois, autant de fois que possible et n'essaye pas d'autre possibilité. C est un raccourci pour C. =item * C S reconnaît au moins C fois et autant de fois que possible et n'essaye pas d'autre possibilité. C est un raccourci pour C et C est un raccourci pour C. =item * C S reconnaît exactement C fois. Il n'existe que pour rendre la notation cohérente. =back Ces quantificateurs possessifs ne sont que des cas spéciaux du concept plus général de I, voir ci-dessous. Comme exemple d'utilisation des quantificateurs possessifs, nous considérons la reconnaissance des chaînes de caractères entre guillemets, telles qu'elles apparaissent dans de nombreux langages. Le backslash est utilisé comme caractère d'échappement pour indiquer que le caractère suivant doit être considéré littéralement, comme n'importe quel autre caractère de la chaîne. Donc, après le guillemet ouvrant, nous attendons une séquence (éventuellement vide) de suites de caractères composées soit de plusieurs caractères différents du guillemet fermant et du backslash, soit d'un caractère précédé du caractère d'échappement (le backslash). /"(?:[^"\\]++|\\.)*+"/; =head2 Construction d'une expression rationnelle À ce point, nous avons couvert tous les concepts de base des expressions rationnelles. Nous pouvons donc présenter un exemple plus complet d'utilisation des expressions rationnelles. Nous allons construire une expression rationnelle qui reconnaît les nombres. La première tâche de la construction d'une expression rationnelle consiste à décider ce que nous voulons reconnaître et ce que nous voulons exclure. Dans notre cas, nous voulons reconnaître à la fois les entiers et les nombres en virgule flottante et nous voulons rejeter les chaînes qui ne sont pas des nombres. La tâche suivante permet de découper le problème en plusieurs petits problèmes qui seront plus simplement transformés en expressions rationnelles. Le cas le plus simple concerne les entiers. Ce sont une suite de chiffres précédée d'un éventuel signe. Les chiffres peuvent être représentés par C<\d+> et le signe peut être reconnu par C<[+-]>. Donc l'expression rationnelle pour les entiers estE: /[+-]?\d+/; # reconnaît les entiers Un nombre en virgule flottante contient potentiellement un signe, une partie entière, un séparateur décimal (un point), une partie décimale et un exposant. Plusieurs de ces parties sont optionnelles donc nous devons vérifier les différentes possibilités. Les nombres en virgule flottante bien formés contiennent entre autres 123., 0.345, .34, -1e6 et 25.4E-72. Comme pour les entiers, le signe au départ est complètement optionnel et peut être reconnu par C<[+-]?>. Nous pouvons voir que s'il n'a pas d'exposant, un nombre en virgule flottante doit contenir un séparateur décimal ou alors c'est un entier. Nous pourrions être tentés d'utiliser C<\d*\.\d*> mais cela pourrait aussi reconnaître un séparateur décimal seul (qui n'est pas un nombre). Donc, les trois cas de nombres sans exposant sontE: /[+-]?\d+\./; # 1., 321., etc. /[+-]?\.\d+/; # .1, .234, etc. /[+-]?\d+\.\d+/; # 1.0, 30.56, etc. On peut combiner cela en une seule expression rationnelleE: /[+-]?(\d+\.\d+|\d+\.|\.\d+)/; # virgule flottante, sans exposant Dans ce choix, il est important de placer C<'\d+\.\d+'> avant C<'\d+\.'>. Si C<'\d+\.'> était en premier, l'expression rationnelle pourrait être reconnue en ignorant la partie décimale du nombre. Considérons maintenant les nombres en virgule flottante avec exposant. Le point clé ici est que les nombres avec séparateur décimal I que les entiers sont autorisés devant un exposant. Ensuite la reconnaissance de l'exposant, comme celle du signe, est indépendante du fait que le nombre possède ou nom une partie décimale. Elle peut donc être découplée de la mantisse. La forme de l'expression rationnelle complète devient donc claire maintenantE: /^(signe optionnel)(entier | mantisse)(exposant optionnel)$/; L'expression est un C ou un C suivi d'un entier. Donc l'expression rationnelle de l'exposant estE: /[eE][+-]?\d+/; # exposant En assemblant toutes les parties ensemble nous obtenons l'expression rationnelle qui reconnaît les nombresE: /^[+-]?(\d+\.\d+|\d+\.|\.\d+|\d+)([eE][+-]?\d+)?$/; # Ta da! De longues expressions rationnelles comme celle-ci peuvent peut-être impressionner vos amis mais elles sont difficiles à déchiffrer. Dans des situations complexes comme celle-ci, le modificateur C est très pratique. Il vous permet de placer des espaces et des commentaires n'importe où dans votre expression sans en changer la signification. En l'utilisant, nous pouvons réécrire notre expression sous une forme plus plaisanteE: /^ [+-]? # en premier, reconnaissance du signe optionnel ( # ensuite reconnaissance d'un entier ou d'un # nombre en virgule flottante \d+\.\d+ # mantisse de la forme a.b |\d+\. # mantisse de la forme a. |\.\d+ # mantisse de la forme .b |\d+ # entier de la forme a ) ([eE][+-]?\d+)? # finalement, reconnaissance optionnelle d'un exposant $/x; Si les espaces ne sont pas pris en compte, comment inclure un caractère espace dans une telle expression rationnelle ? Il suffit de le «backslasher» S> ou de le placer dans une classe de caractères S>. La même chose est valable pour le dièseE: utilisez C<\#> ou C<[#]>. Par exemple, Perl autorise un espace entre le signe et la mantisse ou l'entier. Nous pouvons ajouter cela dans notre expression rationnelle comme suitE: /^ [+-]?\ * # en premier, reconnaissance du signe optionnel *et des espaces* ( # ensuite reconnaissance d'un entier ou d'un # nombre en virgule flottante \d+\.\d+ # mantisse de la forme a.b |\d+\. # mantisse de la forme a. |\.\d+ # mantisse de la forme .b |\d+ # entier de la forme a ) ([eE][+-]?\d+)? # finalement, reconnaissance optionnelle d'un exposant $/x; Sous cette forme, il est plus simple de découvrir un moyen de simplifier le choix. Les branches 1, 2 et 4 du choix commencent toutes par C<\d+> qu'on doit donc pouvoir factoriserE: /^ [+-]?\ * # en premier, reconnaissance du signe optionnel ( # ensuite reconnaissance d'un entier ou d'un # nombre en virgule flottante \d+ # on commence par a ... ( \.\d* # mantisse de la forme a. ou a.b )? # ? pour les entiers de la forme a |\.\d+ # mantisse de la forme .b ) ([eE][+-]?\d+)? # finalement, reconnaissance optionnelle d'un exposant $/x; ou écrit dans sa forme compacteE: /^[+-]?\ *(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/; C'est notre expression rationnelle finale. Pour récapituler, nous avons construit notre expression rationnelleE: =over 4 =item * en spécifiant la tâche en détail, =item * en découpant le problème en plus petites parties, =item * en transformant les petites parties en expressions rationnelles, =item * en combinant les expressions rationnelles, =item * et en optimisant l'expression rationnelle combinée finale. =back On peut faire le parallèle avec les différentes étapes de l'écriture d'un programme. C'est normal puisque les expressions rationnelles sont des programmes écrits dans un petit langage informatique de spécification de motifs. =head2 Utilisation des expressions rationnelles en Perl Pour terminer cette première partie, nous allons examiner brièvement comment les expressions rationnelles sont utilisées dans un programme Perl. Comment s'intègrent-elles à la syntaxe Perl ? Nous avons déjà parlé de l'opérateur de recherche de correspondances dans sa forme par défaut C ou avec des délimiteurs arbitraires C. Nous avons utilisé l'opérateur de mise en correspondance C<=~> ainsi que sa négation C pour rechercher les correspondances. Associé avec l'opérateur de recherche de correspondances, nous avons présenté les modificateurs permettant de traiter de simples lignes C, des multi-lignes C, des expressions insensibles à la casse C et des expressions étendues C. Il y a encore quelques points que vous devez connaître à propos des opérateurs de mise en correspondance. =head3 Optimiser l'évaluation des expressions rationnelles Nous avons montré que les variables étaient substituées avant l'évaluation de l'expression rationnelleE: $pattern = 'Seuss'; while (<>) { print if /$pattern/; } Cela affichera toutes les lignes contenant le mot C. Par contre, ce n'est pas aussi efficace qu'il y paraît car Perl doit réévaluer (ou compiler) C<$pattern> à chaque passage dans la boucle. Si C<$pattern> ne doit pas changer durant la vie du script, nous pouvons ajouter le modificateur C qui demande à Perl de n'effectuer la substitution de variables qu'une seule foisE: #!/usr/bin/perl # grep simple amélioré $regexp = shift; while (<>) { print if /$regexp/o; # cela va plus vite... } =head3 Interdire les substitutions Si vous changez C<$pattern> après la première substitution, Perl l'ignorera. Si vous voulez que perl n'effectue aucune substitution, utilisez le délimiteur spécial CE: @pattern = ('Seuss'); while (<>) { print if m'@pattern'; # correspond littérallement avec '@pattern', # pas avec 'Seuss' } Comme pour les chaînes, C agit exactement comme les apostrophes mais sur les expressions rationnelles ; tout autre délimiteur agit comme des guillemets. Si l'expression rationnelle s'évalue à la chaîne vide, la dernière expression rationnelle I sera utilisée à la place. DoncE: "dog" =~ /d/; # 'd' correspond "dogbert =~ //; # mise en correspondance par # l'expression rationnelle 'd' précédente =head3 Reconnaissance globale Le deux modificateurs restant (C et C) concernent les recherches multiples. Le modificateur C signifie recherche globale et autorise l'opérateur de mise en correspondance à correspondre autant de fois que possible. Dans un contexte scalaire, des invocations successives d'une expression rationnelle modifiée par C appliquée à une même chaîne passeront d'une correspondance à une autre, en se souvenant de la position atteinte dans la chaîne explorée. Vous pouvez consulter ou modifier cette position grâce à la fonction C. L'utilisation de C est montrée dans l'exemple suivant. Supposons une chaîne constituée de mots séparés par des espaces. Si nous connaissons à l'avance le nombre de mots, nous pouvons extraire les mots en utilisant les regroupementsE: $x = "cat dog house"; # 3 mots $x =~ /^\s*(\w+)\s+(\w+)\s+(\w+)\s*$/; # correspond avec # $1 = 'cat' # $2 = 'dog' # $3 = 'house' Mais comment faire si le nombre de mots est indéterminé ? C'est pour ce genre de tâche qu'on a créé C. Pour extraire tous les mots, on utilise l'expression rationnelle simple C<(\w+)> et on boucle sur toutes les correspondances possibles grâce à CE: while ($x =~ /(\w+)/g) { print "Le mot $1 se termine à la position ", pos $x, "\n"; } qui affiche Le mot cat se termine à la position 3 Le mot dog se termine à la position 7 Le mot house se termine à la position 13 Un échec de la mise en correspondance ou un changement de chaîne cible réinitialise la position. Si vous ne voulez pas que la position soit réinitialisée après un échec, ajoutez le modificateur C comme dans C. La position courante dans la chaîne est associée à la chaîne elle-même et non à l'expression rationnelle. Cela signifie que des chaînes différentes ont des positions différentes et que ces positions peuvent être modifiées indépendamment. Dans un contexte de liste, C retourne la liste de tous les groupes mis en correspondance ou, s'il n'y a pas de groupes, la liste de toutes les reconnaissances de l'expression rationnelle entière. Donc si nous ne voulons que les mots, nous pouvons faireE: @words = ($x =~ /(\w+)/g); # correspond avec # $word[0] = 'cat' # $word[1] = 'dog' # $word[2] = 'house' Étroitement associé avec le modificateur C, il existe l'ancre C<\G>. L'ancre C<\G> est mis en correspondance avec le point atteint lors d'une reconnaissance précédente par C. C<\G> permet de faire de la reconnaissance dépendante du contexteE: $metric = 1; # utilisation des unités métriques ... $x = ; # lecture des mesures $x =~ /^([+-]?\d+)\s*/g; # obtention de la valeur $weight = $1; if ($metric) { # vérificiation d'erreur print "Units error!" unless $x =~ /\Gkg\./g; } else { print "Units error!" unless $x =~ /\Glbs\./g; } $x =~ /\G\s+(widget|sprocket)/g; # suite du traitement La combinaison de C et de C<\G> permet le traitement pas à pas d'une chaîne et l'utilisation de Perl pour déterminer la suite du traitement. Actuellement, l'ancre C<\G> n'est réellement utilisable que si elle est utilisée en début de motif. C<\G> est aussi pratique lors du traitement par des expressions rationnelles d'enregistrement à taille fixe. Supposons une séquence d'ADN, encodée comme une suite de lettres C et que vous voulez trouver tous les codons du type C. Dans une séquence, les codons sont des séquences de 3 lettres. Nous pouvons donc considérer une chaîne d'ADN comme une séquence d'enregistrements de 3 lettres. L'expression rationnelle naïveE: # C'est "ATC GTT GAA TGC AAA TGA CAT GAC" $dna = "ATCGTTGAATGCAAATGACATGAC"; $dna =~ /TGA/; ne marche pas ; elle retrouvera un C mais il n'y aura aucune garantie que cette correspondance soit alignée avec une limite de codon, e.g., la sous-chaîne S> donnera une correspondance. Une meilleure solution estE: while ($dna =~ /(\w\w\w)*?TGA/g) { # remarquez le minimal *? print "Got a TGA stop codon at position ", pos $dna, "\n"; } qui afficheraE: Got a TGA stop codon at position 18 Got a TGA stop codon at position 23 La position 18 est correcte mais pas la position 23. Que s'est-il passé ? Notre expression rationnelle fonctionne bien tant qu'il reste de bonnes correspondances. Puis l'expression rationnelle ne trouve plus de C bien synchronisé et réessaie après s'être décalée d'un caractère à chaque fois. Ce n'est pas ce que nous voulons. La solution est l'utilisation de C<\G> pour ancrer notre expression rationnelle sur une limite de codonE: while ($dna =~ /\G(\w\w\w)*?TGA/g) { print "Got a TGA stop codon at position ", pos $dna, "\n"; } qui afficheE: Got a TGA stop codon at position 18 qui est la bonne réponse. Cet exemple illustre qu'il ne suffit pas de reconnaître ce que l'on cherche, il faut aussi rejeter ce qu'on ne veut pas. =head3 Recherche et remplacement Les expressions rationnelles jouent aussi un grand rôle dans les opérations de I en Perl. Une recherche/remplacement est accomplie via l'opérateur C. La forme générale est C avec l'application de tout ce que nous connaissons déjà sur les expressions rationnelles et les modificateurs. La partie C est comme une chaîne entre guillemets de Perl qui remplacera la partie de la chaîne reconnue par l'expression rationnelle C. L'opérateur C<=~> est aussi utilisé pour associer une chaîne avec C. Si on veut l'appliquer à C<$_>, on peut omettre S>. S'il y a correspondance, C renvoie le nombre de substitutions effectuées sinon il retourne faux. Voici quelques exemplesE: $x = "Time to feed the cat!"; $x =~ s/cat/hacker/; # $x contient "Time to feed the hacker!" if ($x =~ s/^(Time.*hacker)!$/$1 now!/) { $more_insistent = 1; } $y = "'quoted words'"; $y =~ s/^'(.*)'$/$1/; # suppression des apostrophes, # $y contient "quoted words" Dans le dernier exemple, la chaîne entière est reconnue mais seule la partie à l'intérieur des apostrophes est dans un groupe. Avec l'opérateur C, les variables C<$1>, C<$2>, etc. sont immédiatement disponibles pour une utilisation dans l'expression de remplacement. Donc, nous utilisons C<$1> pour remplacer la chaîne entre apostrophes par ce qu'elle contient. Avec le modificateur global, C cherchera et remplacera toutes les occurrences de l'expression rationnelle dans la chaîneE: $x = "I batted 4 for 4"; $x =~ s/4/four/; # ne fait pas tout : # $x contient "I batted four for 4" $x = "I batted 4 for 4"; $x =~ s/4/four/g; # fait tout : # $x contient "I batted four for four" Si vous préférez 'regex' à la place de 'regexp' dans ce tutoriel, vous pourriez utiliser le programme suivant pour les remplacerE: % cat > simple_replace #!/usr/bin/perl $regexp = shift; $replacement = shift; while (<>) { s/$regexp/$replacement/go; print; } ^D % simple_replace regexp regex perlretut.pod Dans C nous utilisons le modificateur C pour remplacer toutes les occurrences de l'expression rationnelle à chaque ligne et le modificateur C pour compiler l'expression rationnelle une seule fois pour toutes. Comme avec C, les deux instructions C et C utilisent implicitement la variable C<$_>. Un modificateur spécifique à l'opération de recherche/remplacement est le modificateur d'évaluation C. C ajoute un C autour de la chaîne de remplacement et le résultat de cette évaluation est substitué à la sous-chaîne mise en correspondance. C est utile si vous avez besoin de faire un traitement sur le texte à remplacer. Cet exemple compte la fréquence des lettres dans une ligneE: $x = "Bill the cat"; $x =~ s/(.)/$chars{$1}++;$1/eg; # Le $1 final remplace le caractère par lui-même print "frequency of '$_' is $chars{$_}\n" foreach (sort {$chars{$b} <=> $chars{$a}} keys %chars); qui afficheE: frequency of ' ' is 2 frequency of 't' is 2 frequency of 'l' is 2 frequency of 'B' is 1 frequency of 'c' is 1 frequency of 'e' is 1 frequency of 'h' is 1 frequency of 'i' is 1 frequency of 'a' is 1 Comme pour l'opérateur C, C peut utiliser d'autres délimiteurs comme C ou C et même C. Si les délimiteurs sont des apostrophes C alors l'expression rationnelle et le remplacement sont traités comme des chaînes entre apostrophes et aucune interpolation de variables n'est effectuée. C dans un contexte de liste retourne la même chose qu'en contexte scalaire, c'est à dire le nombre de substitutions effectuées. =head3 La fonction de découpage (split) La fonction C peut, elle aussi, utiliser un opérateur de mise en correspondance C pour découper une chaîne. C découpe la C en une liste de sous-chaînes et retourne cette liste. L'expression rationnelle C est utilisée pour reconnaître la séquence de caractères qui servira de séparateur lors du découpage de C. La C, si elle est présente, indique le nombre maximal de morceaux lors du découpage. Par exemple, pour découper une chaîne en mots, utilisezE: $x = "Calvin and Hobbes"; @words = split /\s+/, $x; # $word[0] = 'Calvin' # $word[1] = 'and' # $word[2] = 'Hobbes' Si l'expression rationnelle vide C est utilisée alors l'expression rationnelle correspond partout et la chaîne est découpée en caractères individuels. Si l'expression rationnelle contient des groupes alors la liste résultante contient aussi les sous-chaînes reconnues par les groupes. Par exempleE: $x = "/usr/bin/perl"; @dirs = split m!/!, $x; # $dirs[0] = '' # $dirs[1] = 'usr' # $dirs[2] = 'bin' # $dirs[3] = 'perl' @parts = split m!(/)!, $x; # $parts[0] = '' # $parts[1] = '/' # $parts[2] = 'usr' # $parts[3] = '/' # $parts[4] = 'bin' # $parts[5] = '/' # $parts[6] = 'perl' Puisque le premier caractère de $X est reconnu par l'expression rationnelle, C ajoute un élément initial vide à la liste. Si vous avez tout lu jusqu'ici, félicitations ! Vous possédez maintenant tous les outils de base pour utiliser les expressions rationnelles afin de résoudre de nombreux problèmes de traitement de textes. Si c'est la première fois que vous lisez ce tutoriel, vous devriez vous arrêter ici et jouer quelques temps avec les expressions rationnelles... La S concerne des aspects plus ésotériques des expressions rationnelles et ces concepts ne sont certainement pas nécessaires au début. =head1 Partie 2: au-delà Bon, vous connaissez les bases des expressions rationnelles et vous voulez en savoir plus. Si la mise en correspondance d'une expression rationnelle est analogue à une marche en forêt alors les outils dont nous avons parlé dans la partie 1 sont la carte et le compas, des outils basiques que nous utilisons tout le temps. La plupart des outils de la partie 2 sont alors analogues à un lance fusées éclairantes ou à un téléphone satellite. On ne les utilise pas très souvent mais, en cas de besoin, ils sont irremplaçables. Ce qui suit présente les fonctionnalités les plus avancées, les moins utilisées ou les plus ésotériques des expressions rationnelles de Perl. Dans cette seconde partie, nous supposerons que vous êtes à l'aise avec les outils de base pour nous concentrer sur ces nouvelles fonctionnalités. =head2 Les plus des caractères, des chaînes et des classes de caractères Il y a de nombreuses séquences d'échappement et classes de caractères dont nous n'avons pas encore parlées. Il existe plusieurs séquences d'échappement qui convertissent les caractères ou les chaînes entre majuscules et minuscules, et elles peuvent être utilisées dans les expressions rationnelles. C<\l> et C<\u> convertissent le caractère suivant respectivement en minuscule ou en majusculeE: $x = "perl"; $string =~ /\u$x/; # reconnaît 'Perl' dans $string $x = "M(rs?|s)\\."; # notez le double backslash $string =~ /\l$x/; # reconnaît 'mr.', 'mrs.' et 'ms.', C<\L> et C<\U> convertissent une sous-chaîne entière délimitée C<\E>, ou stoppée par un autre C<\L> ou C<\U>. $x = "This word is in lower case:\L SHOUT\E"; $x =~ /shout/; # correspond $x = "I STILL KEYPUNCH CARDS FOR MY 360" $x =~ /\Ukeypunch/; # correspond S'il n'y a pas de C<\E>, la changement de casse a lieu jusqu'à la fin de la chaîne. Les expressions rationnelles C<\L\u$word> ou C<\u\L$word> convertissent le premier caractère de C<$word> en majuscule et les autres caractères en minuscules. Les caractères de contrôle peuvent être codés via C<\c>. Le caractère control-z sera mis en correspondance avec C<\cZ>. La séquence d'échappement C<\Q>...C<\E> protège la plupart des caractères non-alphabétiques. Par exemple : $x = "\QThat !^*&%~& cat!"; $x =~ /\Q!^*&%~&\E/; # correspond Les caractères C<$> et C<@> ne sont pas protégés donc les variables peuvent encore être interpolées. Avec Perl 5.6.0, les expressions rationnelles peuvent gérer plus que le jeu de caractères ASCII. Perl gère maintenant I, un standard pour représenter les alphabets de la plupart des langues écrites complétés de nombreux symboles. Les chaînes Perl sont des chaînes Unicode et, de fait, elles peuvent contenir des caractères dont la valeur (le point de code ou numéro de caractère) est supérieure à 255. Quelles conséquences sur les expressions rationnelles ? Les utilisateurs d'expressions rationnelles n'ont pas besoin de connaître la représentation interne des chaînes de perl. Par contre, il faut qu'ils sachent 1) comment représenter les caractères Unicode dans une expression rationnelle et 2) que toutes les opérations de mise en correspondance traiteront une chaîne comme une séquence de caractères Unicode et non comme un séquence d'octets. La réponse à la question 1) est que les caractères Unicode plus grands que C peuvent être représentés en utilisant la notation C<\x{hex}>, sachant que les notations \0 octale et \x hexadécimale (sans les guillemets) ne peuvent pas dépasser 255. /\x{263a}/; # correspond à l'émoticon souriant d'Unicode :) S :> en Perl 5.6.0 il fallait ajouter la directive C pour activer les fonctionnalités Unicode. Ce n'est plus nécessaireE: la quasi totalité des fonctionnalités Unicode ne nécessitent plus la directive ou pragma C. (Le seul cas où cela reste nécessaire est celui où votre script Perl lui-même est écrit en Unicode encodé en UTF-8.) Se souvenir de la séquence hexadécimale d'un caractère Unicode ou décoder les séquences hexadécimales d'un autre dans une expression rationnelle est presque aussi fun que de coder en langage machine. Un autre moyen de spécifier des caractères Unicode est d'utiliser des I via la séquence d'échappement C<\N{nom}>. C est le nom d'un caractère Unicode comme spécifié dans le standard Unicode. Par exemple, si vous voulez représenter ou reconnaître le signe astrologique de la planète Mercure, vous pourriez utiliserE: use charnames ":full"; # utilise les caractères nommés # avec les noms Unicode complet $x = "abc\N{MERCURY}def"; $x =~ /\N{MERCURY}/; # correspond Il est aussi possible d'utiliser les noms courts ou restreints à certains alphabetsE: use charnames ':full'; print "\N{GREEK SMALL LETTER SIGMA} is called sigma.\n"; use charnames ":short"; print "\N{greek:Sigma} is an upper-case sigma.\n"; use charnames qw(greek); print "\N{sigma} is Greek sigma\n"; Une liste de tous les noms complets est disponible dans le fichier Names.txt du répertoire lib/perl5/X.X.X/unicode (où X.X.X est le numéro de la version de perl installée sur votre système). La réponse au point 2), depuis la version 5.6.0, est qu'une expression rationnelle utilise les caractères Unicode. En interne, selon son histoire, une chaîne peut être codée en UTF-8 ou en codage natif sur 8 bits, mais conceptuellement cela reste une séquence de caractères et non d'octets. Voir L pour un tutoriel à ce sujet. Voyons maintenant les classes de caractères Unicode. Comme pour les caractères Unicode, il existe des noms pour les classes de caractères Unicode représentées par la séquence d'échappement C<\p{nom}>. Leur négation C<\P{nom}> existe aussi. Par exemple, pour reconnaître les caractères majuscules et minusculesE: use charnames ":full"; # utilisation des noms Unicode longs $x = "BOB"; $x =~ /^\p{IsUpper}/; # correspond, les caractères majuscules $x =~ /^\P{IsUpper}/; # pas de correspondance, les caractères sans majuscules $x =~ /^\p{IsLower}/; # pas de correspondance, les caractères minuscules $x =~ /^\P{IsLower}/; # correspond, les caractères sans les minuscules Voici les associations entre quelques noms de classes Perl et les classes traditionnelles UnicodeE: Nom de classe Perl Nom de classe Unicode ou expression rationnelle IsAlpha /^[LM]/ IsAlnum /^[LMN]/ IsASCII $code <= 127 IsCntrl /^C/ IsBlank $code =~ /^(0020|0009)$/ || /^Z[^lp]/ IsDigit Nd IsGraph /^([LMNPS]|Co)/ IsLower Ll IsPrint /^([LMNPS]|Co|Zs)/ IsPunct /^P/ IsSpace /^Z/ || ($code =~ /^(0009|000A|000B|000C|000D)$/ IsSpacePerl /^Z/ || ($code =~ /^(0009|000A|000C|000D|0085|2028|2029)$/ IsUpper /^L[ut]/ IsWord /^[LMN]/ || $code eq "005F" IsXDigit $code =~ /^00(3[0-9]|[46][1-6])$/ Vous pouvez aussi utiliser les noms officiels des classes Unicode grâce à C<\p> et C<\P> comme dans C<\p{L}> pour les 'lettres' Unicode, C<\p{Lu}> pour les lettres minuscules ou C<\P{Nd}> pour les non-chiffres. Si C est composé d'une seule lettre, les accolades peuvent être omises. Par exemple C<\pM> est la classe de caractères Unicode 'marks' pour les accents. Pour la liste complète, voir L. Unicode est découpé en plusieurs sous-ensembles de caractères que vous pouvez tester par C<\p{...}> (dans) et C<\P{...}> (hors de). Pour tester si un caractère appartient (ou non) à un système d'écriture, vous pouvez utiliser le nom anglais de ce système, par exemple C<\p{Latin}>, C<\p{Greek}> ou C<\P{Katakana}>. Les autres ensembles sont les blocs Unicode dont le nom commence par "In". L'un de ces blocs est dédié aux opérateurs mathématiques et on peut l'utiliser via C<\p{InMathematicalOperators}>. Pour une liste complète, voir L. C<\X> est une abréviation pour la séquence de classes de caractères qui contient les I Unicode. Une séquence de caractères combinatoires est un caractère de base suivi d'un certain nombre de caractères combinatoires, c'est-à-dire des signes tels que les accents qui changent la prononciation d'une lettre. En utilisant les noms Unicode longs, S> est une séquence de caractères combinatoires avec C comme caractère de base et S> comme caractère de combinatoire et cette séquence représente, en Danois, un A avec un cercle au-dessus comme dans le mot Angstroem. C<\X> est équivalent à C<\PM\pM*> qui signifie un caractère non-marque éventuellement suivi d'une série de caractères marques. Pour obtenir les informations les plus récentes et complètes concernant Unicode, consultez la norme Unicode ou le sit web du consortium Unicode http://www.unicode.org/. Et comme si toutes ces classes ne suffisaient pas, Perl définit aussi des classes de caractères de style POSIX. Elles sont de la forme C<[:nom:]> où C est le nom d'une classe POSIX. Les classes POSIX sont C, C, C, C, C, C, C, C, C, C, C et C plus les deux extensions C (une extension Perl pour reconnaître C<\w>) et C (une extension GNU). Si C est actif alors ces classes sont définies de la même manière que les classes Perl Unicode correspondantesE: C<[:upper:]> est la même chose que C<\p{IsUpper}>, etc. Les classes de caractères POSIX, par contre, ne nécessitent pas l'utilisation de C. Les classes C<[:digit:]>, C<[:word:]> et C<[:space:]> correspondent aux classes familières C<\d>, C<\w> et C<\s>. Pour obtenir la négation d'une classe POSIX, placez un C<^> devant son nom comme dans C<[:^digit:]> qui correspond à C<\D> ou, avec C, à C<\P{IsDigit}>. Les classes Unicode et POSIX peuvent être utilisées comme C<\d>, en sachant que les classes POSIX ne sont accessibles qu'à l'intérieur d'une classe de caractèresE: /\s+[abc[:digit:]xyz]\s*/; # reconnaît a,b,c,x,y,z ou un chiffre /^=item\s[[:digit:]]/; # reconnaît '=item', # suivi d'un espace et d'un chiffre use charnames ":full"; /\s+[abc\p{IsDigit}xyz]\s+/; # reconnaît a,b,c,x,y,z ou un chiffre /^=item\s\p{IsDigit}/; # reconnaît '=item', # suivi d'un espace et d'un chiffre C'est tout pour les caractères et les classes de caractères. =head2 Compilation et stockage d'expressions rationnelles Dans la partie 1, nous avons parlé du modificateur C qui compile une expression rationnelle une seule fois. Cela suggère qu'une expression rationnelle compilée est une sorte de structure de données qui peut être stockée une fois et utilisée plusieurs fois. L'opérateur d'expression rationnelle C fait exactement celaE: C compile la chaîne C en tant qu'expression rationnelle et transforme le résultat en quelque chose qui peut être affectée à une variable. $reg = qr/foo+bar?/; # reg contient une expression rationnelle compilée Puis C<$reg> peut être utilisée en tant qu'expression rationnelleE: $x = "fooooba"; $x =~ $reg; # est reconnu, exactement comme /foo+bar?/ $x =~ /$reg/; # idem, sous une forme différente C<$reg> peut aussi être utilisée au sein d'une expression rationnelle plus grandeE: $x =~ /(abc)?$reg/; # est encore reconnue Comme avec l'opérateur de recherche de correspondances, l'opérateur d'expression rationnelle peut utiliser différents délimiteurs tels C, C ou C. Le délimiteur apostrophe (C) supprime l'interpolation des variables. La pré-compilation des expressions rationnelles est utile pour des mises en correspondances dynamiques qui ne nécessitent pas une recompilation à chaque passage. En utilisant des expressions rationnelles pré-compilées, nous pouvons écrire un programme C qui cherche une suite d'expressions rationnelles en passant à la suivante dès que la précédente est reconnueE: % cat > grep_pas_a_pas #!/usr/bin/perl # grep_pas_a_pas - reconnait regexps, l'une après l'autre # usage: grep_pas_a_pas regexp1 regexp2 ... file1 file2 ... $num = shift; $regexp[$_] = shift foreach (0..$num-1); @compiled = map qr/$_/, @regexp; while ($line = <>) { if ($line =~ /$compiled[0]/) { print $line; shift @compiled; last unless @compiled; } } ^D % grep_pas_a_pas 3 shift print last grep_pas_a_pas $num = shift; print $line; last unless @compiled; Le stockage des expressions rationnelles pré-compilées dans le tableau C<@compiled> nous permet de faire une boucle sur les expressions rationnelles sans les recompiler. On y gagne en flexibilité sans sacrifier la vitesse. =head2 Composition d'expressions rationnelles durant l'exécution Les retours arrières sont plus efficaces que des essais successifs avec plusieurs expressions rationnelles. Si vous avez plusieurs expressions rationnelles et que la reconnaissance d'une seule d'entre elles est acceptable alors il est possible de les combiner sous la forme d'une alternative. Si les expressions initiales sont des données d'entrée, cela peut être fait via une opération de jointure. Nous allons exploiter cette idée dans une version améliorée du programme C, un programme qui reconnaît plusieurs motifsE: % cat > multi_grep #!/usr/bin/perl # multi_grep - reconnaît une regexp parmi regexps # usage: multi_grep regexp1 regexp2 ... file1 file2 ... $num = shift; $regexp[$_] = shift foreach (0..$num-1); $pattern = join '|', @regexp; while ($line = <>) { print $line if $line =~ /$pattern/o; } ^D % multi_grep 2 shift for multi_grep $num = shift; $regexp[$_] = shift foreach (0..$num-1); Il est parfois avantageux de construire le motif à partir de l'I qui doit être analysée et d'utiliser les valeurs autorisées dans la partie gauche de l'opérateur de reconnaissance. Comme exemple de cette situation apparement paradoxale, nous allons supposer que notre entrée contient une commande qui doit correspondre à l'une des commandes autorisées mais en ajoutant que cette commande peut être abrégée s'il n'y a pas d'ambiguïté possible avec une autre commande. Le programme ci-dessous illustre l'algorithme de S % cat > keymatch #!/usr/bin/perl $kwds = 'copy compare list print'; while( $command = <> ){ $command =~ s/^\s+|\s+$//g; # trim leading and trailing spaces if( ( @matches = $kwds =~ /\b$command\w*/g ) == 1 ){ print "command: '$matches'\n"; } elsif( @matches == 0 ){ print "no such command: '$command'\n"; } else { print "not unique: '$command' (could be one of: @matches)\n"; } } ^D % keymatch li command: 'list' co not unique: 'co' (could be one of: copy compare) printer no such command: 'printer' Plutôt que d'essayer de reconnaître les commandes existantes à l'intérieur de l'entrée fournie, nous essayons de reconnaître cette entrée dans l'ensemble des commandes. L'opération de mise en correspondance S> fait plusieurs choses à la fois. Elle s'assure que la commande fournie commence bien là où un mot clé débute (C<\b>). Elle accepte les abréviations grâce à C<\w*>. Elle indique le nombre de correspondances (C) et donne tous les mots clés réellement reconnus. Vous pouvez difficilement demander plus. =head2 Commentaires et modificateurs intégrés dans une expression rationnelle À partir d'ici, nous allons parler des I de Perl. Ce sont des extensions de la syntaxe traditionnelle des expressions rationnelles qui fournissent de nouveaux outils utiles pour la recherche de motifs. Nous avons déjà vu des extensions telles que les quantificateurs minimaux C, C<*?>, C<+?>, C<{n,m}?> et C<{n,}?>. Les autres extensions sont toutes de la forme C<(?car...)> où C est un caractère qui détermine le type de l'extension. La première extension est un commentaire C<(?#texte)>. Cela permet d'inclure un commentaire dans l'expression rationnelle sans modifier sa signification. Le commentaire ne doit pas contenir de parenthèse fermante dans son texte. Un exempleE: /(?# reconnaît un entier:)[+-]?\d+/; Ce style de commentaires a été largement amélioré grâce au modificateur C qui permet des commentaires beaucoup plus libres et simples. Les modificateurs C, C, C et C (ou une combinaison de plusieurs d'entre eux) peuvent eux aussi être intégrés dans une expression rationnelle en utilisant C<(?i)>, C<(?m)>, C<(?s)> et C<(?x)>. Par exempleE: /(?i)yes/; # reconnaît 'yes' sans tenir compte de la casse /yes/i; # la même chose /(?x)( [+-]? # un signe optionnel \d+ # les chiffres ) /x; Les modificateurs inclus ont deux avantages importants sur les modificateurs habituels. Tout d'abord, ils permettent de spécifier une combinaison de modificateurs différente pour I partie de l'expression rationnelle. C'est pratique pour mettre en correspondance un tableau d'expressions rationnelles qui ont des modificateurs différentsE: $pattern[0] = '(?i)doctor'; $pattern[1] = 'Johnson'; ... while (<>) { foreach $patt (@pattern) { print if /$patt/; } } Ensuite parce que les modificateurs inclus n'agissent que sur le groupe dans lequel ils apparaissent (sauf C qui modifie l'expression rationnelle dans son ensemble). Donc, le regroupement peut être utiliser pour «localiser» l'effet des modificateursE: /Answer: ((?i)yes)/; # reconnaît 'Answer: yes', 'Answer: YES', etc. Les modificateurs inclus peuvent aussi annuler des modificateurs déjà présents en utilisant par exemple C<(?-i)>. Les modificateurs peuvent être combinés en une seule expression comme C<(?s-i)> qui active le mode simple ligne et annule l'insensibilité à la casse. Les modificateurs inclus sont aussi utilisables dans un regroupement sans mémorisation. C<(?i-m:regexp)> est un regroupement sans mémorisation qui reconnaît C sans être sensible à la casse et en désactivant le mode multi-lignes. =head2 Regarder en arrière et regarder en avant Cette section présente les assertions permettant de regarder en arrière ou en avant. Tout d'abord quelques petits éléments d'introduction. Dans les expressions rationnelles en Perl, la plupart des éléments 'consomment' une certaine quantité de la chaîne avec lesquels ils sont mis en correspondance. Par exemple, l'élément C<[abc]> consomme un caractère de la chaîne lorsqu'il est reconnu, dans le sens où Perl avance au caractère suivant dans la chaîne après la mise en correspondance. Il existe certains éléments, par contre, qui ne consomment aucun caractère (il ne change pas la position) lorsqu'ils sont reconnus. Les exemples que nous avons déjà vus sont des ancres. L'ancre C<^> reconnaît le début de ligne mais ne consomme aucun caractère. De même, l'ancre de bordure de mot C<\b> est reconnue entre un caractère qui est reconnu par C<\w> et un caractère qui ne l'est pas mais elle ne consomme aucun caractère. Ces ancres sont des exemples d'assertions de longueur nulle. « De longueur nulle » parce qu'elles ne consomment aucun caractère et « assertion » parce qu'elles testent une propriété de la chaîne. Dans le contexte de notre analogie avec la traversée d'une forêt, la plupart des éléments des expressions rationnelles nous font avancer le long du chemin alors que les ancres nous arrêtent pour regarder autour de nous. Si l'environnement local convient, nous pouvons continuer. Sinon, il faut effectuer un retour arrière. Vérifier l'environnement peut impliquer un regard en avant, en arrière ou les deux. C<^> regarde en arrière pour vérifier qu'il n'y a aucun caractère avant. C<$> regarde en avant pour vérifier qu'il n'y a pas de caractère après. C<\b> regarde en avant et en arrière pour vérifier que les deux caractères qui l'entourent sont de natures différentes (mot et non-mot). Les assertions en avant et en arrière sont des généralisations du concept d'ancres. Ce sont des assertions de longueur nulle qui nous permettent de spécifier les caractères que nous voulons. L'assertion de regard en avant est notée C<(?=regexp)> alors que l'assertion de regard en arrière est notée C<< (?<=fixed-regexp) >>. Voici quelques exemplesE: $x = "I catch the housecat 'Tom-cat' with catnip"; $x =~ /cat(?=\s)/; # reconnaît 'cat' dans 'housecat' @catwords = ($x =~ /(?<=\s)cat\w+/g); # correspond avec # $catwords[0] = 'catch' # $catwords[1] = 'catnip' $x =~ /\bcat\b/; # reconnaît 'cat' dans 'Tom-cat' $x =~ /(?<=\s)cat(?=\s)/; # pas de correspondance; pas de 'cat' isolé # au milieu de $x Notez que les parenthèses dans C<(?=regexp)> et C<< (?<=regexp) >> sont sans mémorisation puisque ce sont des assertions de longueur nulle. Donc, dans le deuxième exemple, les sous-chaînes capturées sont celles reconnues par l'expression rationnelle complète. Les assertions en avant C<(?=regexp)> peuvent reconnaître une expression rationnelle quelconque. Par contre, les assertions en arrière C<< (?<=fixed-regexp) >> ne fonctionnent que pour des expressions rationnelles de longueur fixe (dont le nombre de caractères est fixé). Donc C<< (?<=(ab|bc)) >> est correct mais pas C<< (?<=(ab)*) >>. Les négations de ces assertions se notent respectivement C<(?!regexp)> et C<< (?>. Elles sont évaluées à vrai si l'expression rationnelle ne correspond IE: $x = "foobar"; $x =~ /foo(?!bar)/; # pas de correspondance, 'bar' suit 'foo' $x =~ /foo(?!baz)/; # reconnue, 'baz' ne suit pas 'foo' $x =~ /(? n'est pas utilisable dans une assertion en arrière parce que cette classe utilise une tricherie sur la définition d'un caractère qui le deviendrait encore plus en regardant vers l'arrière. Voici un exemple où une chaîne doit être découpée en retrouvant chaque mot et chaque nombre séparés par des espaces et chacun des tirets considérés isolément. Si on tente d'utiliser C, cela ne marche pas car les espaces ne sont pas nécessaires entre les tirets ou entre un mot et un tiret. Les points de découpe supplémentaire sont identifiés en regardant en avant et en arrière. $str = "one two - --6-8"; @toks = split / \s+ # un groupe d'espaces | (?<=\S) (?=-) # un non-espace suivi d'un '-' | (?<=-) (?=\S) # un '-' suivi d'un non-espace /x, $str; # @toks = qw(one two - - - 6 - 8) =head2 Utilisation de sous-expressions indépendantes pour empêcher les retours arrière Les I sont des expressions rationnelles qui, dans le contexte d'une expression rationnelle plus grande, fonctionnent indépendamment de cette expression rationnelle globale. C'est à dire qu'elles consomment tout ce qu'elles veulent de la chaîne sans prendre en compte la possibilité de reconnaissance de l'expression rationnelle globale. Les sous-expressions indépendantes sont représentées par C<< (?>regexp) >>. Pour illustrer leur comportement, considérons tout d'abord une expression rationnelle ordinaireE: $x = "ab"; $x =~ /a*ab/; # correspondance Il y a évidemment correspondance mais lors de la reconnaissance, la sous-expression C consomme d'abord le C. Ce faisant, elle empêche la reconnaissance de l'expression rationnelle globale. Donc, après retour arrière, C laisse le C et reconnaît la chaîne vide. Ici, ce que C a reconnu est I de ce qui est reconnu par le reste de l'expression rationnelle. En revanche, considérons l'expression rationnelle avec une sous-expression indépendanteE: $x =~ /(?>a*)ab/; # pas de correspondance La sous-expression indépendante C<< (?>a*) >> ne tiens pas compte du reste de l'expression rationnelle et donc consomme le C. Le reste de l'expression rationnelle C ne peut plus trouver de correspondances. Puisque C<< (?>a*) >> est indépendante, il n'y a pas de retour arrière et la sous-expression indépendante ne relâche pas son C. Donc l'expression rationnelle globale n'est pas reconnue. On observe un comportement similaire avec deux expressions rationnelles complètement indépendantesE: $x = "ab"; $x =~ /a*/g; # est reconnue, consomme le 'a' $x =~ /\Gab/g; # pas de correspondance, plus de 'a' disponible Ici C et C<\G> créent un point de non-retour entre les deux expressions rationnelles. Les expressions rationnelles avec des sous-expressions indépendantes font un peu la même chose en plaçant un point de non-retour après la reconnaissance d'une sous-expression indépendante. Cette possibilité de blocage du retour arrière grâce aux sous-expressions indépendantes est très pratique. Supposons que nous voulons reconnaître une chaîne non-vide entre parenthèses (pouvant contenir elle-même un niveau de parenthèses). L'expression rationnelle suivante fonctionneraE: $x = "abc(de(fg)h"; # parenthèse non refermée $x =~ /\( ( [^()]+ | \([^()]*\) )+ \)/x; L'expression rationnelle reconnaît une parenthèse ouvrante, une ou plusieurs occurrences d'une alternative et une parenthèse fermante. La première branche de l'alternative C<[^()]+> reconnaît un sous-chaîne sans parenthèse et la seconde branche C<\([^()]*\)> reconnaît une sous-chaîne entourée de parenthèses. Cette expression rationnelle est malheureusement pathologiqueE: elle contient des quantificateurs imbriqués de la forme C<(a+|b)+>. Nous avons expliqué dans la partie 1 pourquoi une telle imbrication impliquait un temps d'exécution exponentiel en cas de non-reconnaissance. Pour prévenir cette explosion combinatoire, nous devons empêcher les retours arrière inutiles quelque part. On peut y arriver en incluant le quantificateur interne dans une sous-expression indépendanteE: $x =~ /\( ( (?>[^()]+) | \([^()]*\) )+ \)/x; Ici, C<< (?>[^()]+) >> interrompt le partitionnement combinatoire de la chaîne en consommant la partie la plus grande possible de la chaîne sans permettre le retour arrière. Donc, la non-reconnaissance sera détectée beaucoup plus rapidement. =head2 Les expressions conditionnelles Une I est une forme d'instruction si-alors-sinon qui permet de choisir entre deux motifs à reconnaître selon une condition. Il existe deux types d'expressions conditionnellesE: C<(?(condition)motif-oui)> et C<(?(condition)motif-oui|motif-non)>. C<(?(condition)motif-oui)> agit comme une instruction S> en Perl. Si la C est vraie, le C doit être reconnu. Si la C est fausse, le C n'est pas pris en compte et Perl passe à l'élément suivant dans l'expression rationnelle. La seconde forme est comme une instruction S> en Perl. Si la C est vraie le C doit être reconnu sinon c'est le C qui doit être reconnu. La C a plusieurs formes possibles. La première forme est simplement un entier entre parenthèses C<(entier)>. La condition est vraie si le regroupement mémorisé correspondant C<\entier> a été reconnue plus tôt dans l'expression rationnelle. On peut faire la même chose avec un nom associé à un regroupement mémorisé en écrivant soit C<< () >> soit C<< ('nom') >>. La seconde forme est une assertion de longueur nulle C<(?...)>, soit en avant, soit en arrière, soit une assertion de code (dont on parlera dans la section suivante). La troisième forme fournit des tests qui retournent vrai si l'expression est évaluée à l'intérieur d'une récursion (C<(R)>) ou est appelée depuis une regroupement mémorisé référencé par son numéro (C<(R1)>, C<(R2)>,...) ou par son nom (C<(R&nom)>). La forme de la C utilisant un entier ou un nom nous permet de choisir, avec plus de flexibilité, ce que l'on veut reconnaître en fonction de ce qui a déjà été reconnu. L'exemple suivant cherche les mots de la forme C<"$x$x"> ou C<"$x$y$y$x">E: % simple_grep '^(\w+)(\w+)?(?(2)\2\1|\1)$' /usr/dict/words beriberi coco couscous deed ... toot toto tutu La C sous la forme d'une assertion de longueur nulle en arrière, combinée avec des références arrières, permet à une partie de la reconnaissance d'influencer une autre partie de la reconnaissance qui arrive plus tard. Par exempleE: /[ATGC]+(?(?<=AA)G|C)$/; reconnaît une séquence d'ADN qui ne se termine ni par C ni par C. Remarquez la notation C<< (?(?<=AA)G|C) >> et non pas C<< (?((?<=AA))G|C) >> ; pour les assertions de longueur nulle, les parenthèses ne sont pas nécessaires. =head2 Définir des motifs nommés Certaines expressions rationnelles doivent réutiliser plusieurs fois le même sous-motif en différents endroits. Depuis Perl 5.10, il est possible de définir des sous-motifs nommés afin d'y faire appel n'importe où dans l'expression rationnelle les contenant. La syntaxe pour définir un tel groupe est C<< (?(DEFINE)(?motif)...) >>. L'appel d'un tel groupe s'écrit C<(?&nom)>. L'exemple ci-dessous illustre cette fonctionnalité en s'appuyant sur le motif des nombres à virgule flottante présenté précédement. Les trois sous-motifs utilisés plus d'une fois sont le signe optionnel (osg), la suite de chiffres pour un entier (int) et la partie décimale (dec). Le groupe DEFINE à la fin contient leur définition. Notez que le motif pour la partie décimale est le premier endroit où nous pouvons réutiliser le motif pour un entier. /^ (?&osg)\ * ( (?&int)(?&dec)? | (?&dec) ) (?: [eE](?&osg)(?&int) )? $ (?(DEFINE) (?[-+]?) # signe optionnel (?\d++) # entier (?\.(?&int)) # partie décimale )/x =head2 Motifs récursifs Cette fonctionnalité (apparue dans Perl 5.10) augmente beaucoup la puissance des expressions rationnelles de Perl. En se référant à un regroupement avec mémorisation via la construction C<(?groupe-ref)>, le I du regroupement référencé est utilisé comme un sous-motif indépendant à l'endroit où est placée la référence. Comme cette référence peut appraître I le regroupement auquel elle fait référence, cela rend possible l'utilisation d'expressions rationnelles pour des tâche qui, jusqu'ici, nécessitait un analyseur récursif. Pour illustrer cette fonctionnalité, nous allons construire un motif qui reconnaît un palindrome (c'est un mot ou une phrase qui, en faisant abstraction des espaces et de la ponctuation, se lit de la même manière à l'endroit et à l'envers). Nous commençons par constater qu'une chaîne vide ou ne contenant qu'un seul caractère est un palindrome. Sinon elle doit avoir un caractère mot au début, ce même caractère mot à la fin et un palindrome entre les deux. /(?: (\w) (?...ici un palindrome...) \{-1} | \w? )/x En ajoutant C<\W*> aux deux extrémités pour supprimer ce qui peut être ignorer, on obtient le motif S my $pp = qr/^(\W* (?: (\w) (?1) \g{-1} | \w? ) \W*)$/ix; for $s ( "saippuakauppias", "A man, a plan, a canal: Panama!" ){ print "'$s' is a palindrome\n" if $s =~ /$pp/; } Dans la construction C<(?...)>, on peut utiliser une référence absolue ou relative. Le motif complet peut être réinséré via C<(?R)> ou C<(?0)>. Si vous préférez nommer vos regroupements, vous pouvez aussi utiliser C<(?&nom)> pour les utiliser récursivement. =head2 Un peu de magieE: exécution de code Perl dans une expression rationnelle Normalement les expressions rationnelles sont une partie d'une expression Perl. Les expressions d'I<évaluation de code> renversent cela en permettant de placer n'importe quel code Perl à l'intérieur d'une expression rationnelle. Une expression d'évaluation de code est notée C<(?{code})> où I est une chaîne contenant des instructions Perl. Notez que cette fonctionnalité est encore considérée comme expérimentale et peut donc changer sans avertissement. Une expression d'évaluation de code est une assertion de longueur nulle et la valeur qu'elle retourne dépend de son environnement. Il y a deux possibilités : soit l'expression est utilisée comme condition dans une expression conditionnelle C<(?(condition)...)> soit ce n'est pas le cas. Si l'expression d'évaluation de code est une condition, le code est évalué et son résultat (c'est à dire le résultat de la dernière instruction) est utilisé pour déterminer la véracité de la condition. Si l'expression d'évaluation de code n'est pas une condition, l'assertion est toujours vraie et le résultat du code est stocké dans la variable spéciale C<$^R>. Cette variable peut être utilisée dans des expressions d'évaluation de code apparaissant plus tard dans l'expression rationnelle. Voici quelques exemplesE: $x = "abcdef"; $x =~ /abc(?{print "Hi Mom!";})def/; # reconnue, # affiche 'Hi Mom!' $x =~ /aaa(?{print "Hi Mom!";})def/; # non reconnue, # pas de 'Hi Mom!' Attention à l'exemple suivantE: $x =~ /abc(?{print "Hi Mom!";})ddd/; # non reconnue, # pas de 'Hi Mom!' # mais pourquoi ? En première approximation, vous pourriez croire qu'il n'y a pas d'affichage parce que C ne peut être reconnue dans la chaîne explorée. Mais regardez l'exemple qui suitE: $x =~ /abc(?{print "Hi Mom!";})[d]dd/; # non reconnue, # mais _affiche_ 'Hi Mom!' Que s'est-il passé dans ce cas ? Si vous avez tout suivi jusqu'ici, vous savez que les deux expressions rationnelles précédentes sont équivalentes -- enfermer le d dans une classe de caractères ne change en rien ce qui peut être reconnu. Alors pourquoi l'une n'affiche rien alors que l'autre le fait ? La réponse est liée aux optimisations faites par le moteur d'expressions rationnelles. Dans le premier cas, tout ce que le moteur voit c'est une série de caractères simples (mis à part la construction C). Il est assez habile pour s'apercevoir que la chaîne 'ddd' n'apparaît pas dans la chaîne explorée avant même de commencer son exploration. Alors que dans le second cas, nous avons utilisé une astuce pour lui faire croire que notre motif était plus compliqué qu'il ne l'est. Il voit donc notre classe de caractères et décide qu'il doit commencer l'exploration pour déterminer si notre expression rationnelle peut ou non être reconnue. Et lors de cette exploration, il atteint l'instruction d'affichage avant de s'apercevoir qu'il n'y a pas de correspondance possible. Pour en savoir un peu plus sur les optimisations faites par le moteur, reportez-vous à la section L<"Directives (pragma) et déverminage"> plus bas. D'autres exemples avec C: $x =~ /(?{print "Hi Mom!";})/; # reconnue, # affiche 'Hi Mom!' $x =~ /(?{$c = 1;})(?{print "$c";})/; # reconnue, # affiche '1' $x =~ /(?{$c = 1;})(?{print "$^R";})/; # reconnue, # affiche '1' La magie évoquée dans le titre de cette section apparaît lorsque le processus de recherche de correspondance effectue un retour arrière. Si le retour arrière implique une expression d'évaluation de code et si les variables modifiées par ce code ont été localisées (via C) alors les modifications faites sur ces variables sont annulées ! Donc, si vous voulez compter le nombre de fois ou un caractère à été reconnu lors d'une mise en correspondance, vous pouvez utiliser le code suivantE: $x = "aaaa"; $count = 0; # initialisation du compteur de 'a' $c = "bob"; # pour montrer que $c n'est pas modifié $x =~ /(?{local $c = 0;}) # initialisation du compteur ( a # 'a' est reconnu (?{local $c = $c + 1;}) # incrémentation du compteur )* # on répète cela autant que possible aa # mais on reconnaît 'aa' à la fin (?{$count = $c;}) # recopie de $c local dans $count /x; print "'a' count is $count, \$c variable is '$c'\n"; Ce code afficheE: 'a' count is 2, $c variable is 'bob' Si nous remplaçons S> par S>, les modifications faites à la variable ne sont I annulées lors du retour arrière et nous obtenonsE: 'a' count is 4, $c variable is 'bob' Notez bien que seules les modifications aux variables localisées sont annulées. Les autres effets secondaires de l'exécution du code sont permanents. DoncE: $x = "aaaa"; $x =~ /(a(?{print "Yow\n";}))*aa/; produitE: Yow Yow Yow Yow Le résultat C<$^R> est automatiquement localisés et donc il se comporte bien lors de retours arrière. Cet exemple utilise une expression d'évaluation de code dans une condition pour reconnaître un article défini, 'the' pour l'anglais ou 'der|die|das' pour l'allemandE: $lang = 'DE'; # en allemand ... $text = "das"; print "matched\n" if $text =~ /(?(?{ $lang eq 'EN'; # est-on en anglais ? }) the | # si oui, alors il faut reconnaître 'the' (der|die|das) # sinon, reconnaître 'die|das|der' ) /xi; Remarquez ici que la syntaxe est C<(?(?{...})motif-oui|motif-non)> et non pas C<(?((?{...}))motif-oui|motif-non)>. En d'autres termes, dans le cas d'une expression d'évaluation de code, les parenthèses autour de la condition sont optionnelles. Si vous tentez d'utiliser des expressions d'évaluation de code combinées avec des variables interpolées, Perl risque de vous surprendreE: $bar = 5; $pat = '(?{ 1 })'; /foo(?{ $bar })bar/; # se compile bien, $bar n'est pas interpolée /foo(?{ 1 })$bar/; # erreur de compilation ! /foo${pat}bar/; # erreur de compilation ! $pat = qr/(?{ $foo = 1 })/; # précompilation d'une expression /foo${pat}bar/; # se compile bien Si une expression rationnelle contient soit des expressions d'évaluation de code et des variables interpolées, soit une variable dont l'interpolation donne une expression d'évaluation de code, Perl traite cela comme une erreur dans l'expression rationnelle. En revanche, si l'expression d'évaluation de code est précompilée dans une variable, l'interpolation fonctionne. Pourquoi cette erreur ? C'est parce que la combinaison de l'interpolation de variables et des expressions d'évaluation de code est risquée. Elle est risquée parce que beaucoup de programmeurs qui écrivent des moteurs de recherche utilisent directement les valeurs fournies par l'utilisateur dans leurs expressions rationnellesE: $regexp = <>; # lecture de l'expression rationnelle de l'utilisateur $chomp $regexp; # suppression d'un éventuel passage à la ligne $text =~ /$regexp/; # recherche de $regexp dans $text Si la variable C<$regexp> pouvait contenir des expressions d'évaluation de code, l'utilisateur pourrait exécuter n'importe quel code Perl. Par exemple, un petit rigolo pourrait chercher S> pour effacer tous vos fichiers. En ce sens, la combinaison de l'interpolation de variables et des expressions d'évaluation de code I votre expression rationnelle. Donc, par défaut, cette combinaison n'est pas autorisée. Si vous n'êtes pas concerné par les utilisateurs malicieux, il est possible de désactiver cette interdiction en invoquant S :> use re 'eval'; # pas de sécurité $bar = 5; $pat = '(?{ 1 })'; /foo(?{ 1 })$bar/; # se compile bien /foo${pat}bar/; # se compile bien Une autre forme d'expressions impliquant une évaluation de code est l'I. Cette expression est comme la précédente sauf que le résultat de l'évaluation du code est traité comme un élément d'expression rationnelle à mettre en correspondance immédiatement. Voici un exemple simpleE: $length = 5; $char = 'a'; $x = 'aaaaabb'; $x =~ /(??{$char x $length})/x; # reconnue, il y a bien 5 'a' Le dernier exemple contient à la fois des expressions ordinaires et des expressions impliquant de l'évaluation de code. Il détecte si les espacements entre les C<1> d'une chaîne binaire C<1101010010001...> respectent une suite de Fibonacci 0, 1, 1, 2, 3, 5... $x = "1101010010001000001"; $s0 = 0; $s1 = 1; # conditions initiales print "C'est une suite de Fibonacci\n" if $x =~ /^1 # on reconnaît le '1' initial (?: ((??{ $z0 })) # match some '0' 1 # and then a '1' (?{ $z0 = $z1; $z1 .= $^N; }) )+ # répété autant que nécessaire $ # c'est la fin /x; printf "La séquence la plus large mesure %d\n", length($z1)-length($z0); Souvenez-vous que C<$^N> contient ce qui a été reconnu par le dernier regroupement complétement reconnu. Cela afficheE: C'est une suite de Fibonacci La séquence la plus large mesure 5 Remarquez que les variables C<$z0> et C<$z1> ne sont pas interpolées lorsque l'expression rationnelle est compilée comme cela se passerait pour des variables en dehors d'une expression d'évaluation de code. En fait, le code est évalué à chaque fois que Perl le rencontre lors de la mise en correspondance. Cette expression rationnelle sans le modificateur C estE: /^1(?:((??{ $z0 }))1(?{ $z0 = $z1; $z1 .= $^N; }))+$/ Cela montre que l'usage d'espaces dans la partie code est encore possible. Néanmoins lorsqu'on travaille avec du code et des expressions conditionnelles, la forme étendue des expressions rationnelles est quasiment toujours nécessaire pour en faciliter la création et la maintenance. =head2 Ordres de contrôle du retour-arrière Perl 5.10 propose un certain nombre d'ordres de contrôle permettant de piloter finement le processus de retour-arrière en influençant directement le moteur d'expressions rationnelles et en fournissant des moyens de surveillance. Comme d'habitude ces nouvelles fonctionnalités sont expérimentales et sujettent à changement voire suppression dans de futures versions de Perl. Le lecteur interessé pourra se référer à L pour une description plus détaillée. Ci-dessous vous trouverez un simple exemple qui illustre l'usage de l'ordre C<(*FAIL)> qui s'abrège en C<(*F)>. Si cet ordre est inséré dans une expression rationnelle, il la fera échouer exactement comme le ferait une non correspondance entre la chaîne et le motif recherché. Le traitement de l'expression rationnelle continue comme après un échec "normal" et donc, par exemple, le moteur essaye une autre branche d'une alternative ou avance à une position suivante dans la chaîne. Comme pour un échec ne préserve pas les groupes ou les résultats produits, il peut être nécessaire de coupler cet ordre avec du code embarqué. %count = (); "supercalifragilisticexpialidoceous" =~ /([aeiou])(?{ $count{$1}++; })(*FAIL)/oi; printf "%3d '%s'\n", $count{$_}, $_ for (sort keys %count); Le motif commence par une classe de caractères contenant un sous-ensemble des lettres. À chaque fois que cette classe est reconnue, l'instruction C<$count{'a'}++;> est exécutée pour incrémenter les compteurs de lettres. Puis l'ordre C<(*FAIL)> est rencontré et le moteur d'expression rationnelle effectue ce qu'on lui demandeE: tant qu'il ne rencontre pas la fin de la chaîne, il avance dans la chaîne à la recherche d'une autre voyelle. Donc, qu'il y ait ou non reconnaissance importe peu : le moteur travaille jusqu'à l'inspection complète de la chaîne. Il faut noter que la solution S $count{lc($_)}++ for split('', "supercalifragilisticexpialidoceous"); printf "%3d '%s'\n", $count2{$_}, $_ for ( qw{ a e i o u } ); est considérablement plus lente. =head2 Directives (pragma) et déverminage Il existe plusieurs directives (pragma) pour contrôler et déboguer les expressions rationnelles en Perl. Nous avons déjà rencontré une directive dans la section précédente, S>, qui autorise la coexistence de variables interpolées et d'expressions d'évaluation de code dans la même expression rationnelle. Les autres directives sontE: use re 'taint'; $tainted = <>; @parts = ($tainted =~ /(\w+)\s+(\w+)/; # @parts est maintenant souillé La directive C rend souillées les sous-chaînes extraites lors d'une mise en correspondance appliquée à une chaîne souillée. Ce n'est pas le cas par défaut puisque les expressions rationnelles sont souvent utilisées pour extraire une information sûre d'une chaîne souillée. Utilisez C lorsque l'extraction ne garantit pas la sûreté de l'information extraite. Les deux directives C et C ont une portée lexicale limitée au bloc qui les englobe. use re 'debug'; /^(.*)$/s; # affichage d'information de debug use re 'debugcolor'; /^(.*)$/s; # affichage d'information de debug en couleur Les directives globales C et C vous permettent d'obtenir des informations détaillées lors de la compilation et de l'exécution des expressions rationnelles. C est exactement comme C sauf que les informations sont affichées en couleur sur les terminaux qui sont capables d'afficher les séquences termcap de colorisation. Voici quelques exemples de sortiesE: % perl -e 'use re "debug"; "abc" =~ /a*b+c/;' Compiling REx `a*b+c' size 9 first at 1 1: STAR(4) 2: EXACT (0) 4: PLUS(7) 5: EXACT (0) 7: EXACT (9) 9: END(0) floating `bc' at 0..2147483647 (checking floating) minlen 2 Guessing start of match, REx `a*b+c' against `abc'... Found floating substr `bc' at offset 1... Guessed: match at offset 0 Matching REx `a*b+c' against `abc' Setting an EVAL scope, savestack=3 0 <> | 1: STAR EXACT can match 1 times out of 32767... Setting an EVAL scope, savestack=3 1 | 4: PLUS EXACT can match 1 times out of 32767... Setting an EVAL scope, savestack=3 2 | 7: EXACT 3 <> | 9: END Match successful! Freeing REx: `a*b+c' Si vous êtes arrivés aussi loin dans ce tutoriel, vous pouvez probablement comprendre à quoi correspondent les différentes parties de cette sortie. La première partieE: Compiling REx `a*b+c' size 9 first at 1 1: STAR(4) 2: EXACT (0) 4: PLUS(7) 5: EXACT (0) 7: EXACT (9) 9: END(0) décrit la phase de compilation. C signifie qu'il y a un objet étoilé, dans ce cas 'a', et qu'une fois reconnu, il faut aller en 4, c'est à dire C. Les lignes du milieu décrivent des heuristiques et des optimisations appliquées avant la mise en correspondanceE: floating `bc' at 0..2147483647 (checking floating) minlen 2 Guessing start of match, REx `a*b+c' against `abc'... Found floating substr `bc' at offset 1... Guessed: match at offset 0 Puis la mise en correspondance est effectuée et les lignes qui restent décrivent ce processusE: Matching REx `a*b+c' against `abc' Setting an EVAL scope, savestack=3 0 <> | 1: STAR EXACT can match 1 times out of 32767... Setting an EVAL scope, savestack=3 1 | 4: PLUS EXACT can match 1 times out of 32767... Setting an EVAL scope, savestack=3 2 | 7: EXACT 3 <> | 9: END Match successful! Freeing REx: `a*b+c' Chaque pas est de la forme S >>> où C<< >> est la partie de la chaîne reconnue et C<< >> est la partie de la chaîne non encore reconnue. S>> indique que Perl est rendu à la ligne 1 de la phase de compilation vue précédemment. Voir L pour de plus amples informations. Une autre méthode pour déboguer les expressions rationnelles consiste à inclure des instructions C dans l'expression rationnelle. "that this" =~ m@(?{print "Start at position ", pos, "\n";}) t(?{print "t1\n";}) h(?{print "h1\n";}) i(?{print "i1\n";}) s(?{print "s1\n";}) | t(?{print "t2\n";}) h(?{print "h2\n";}) a(?{print "a2\n";}) t(?{print "t2\n";}) (?{print "Done at position ", pos, "\n";}) @x; qui afficheE: Start at position 0 t1 h1 t2 h2 a2 t2 Done at position 4 =head1 BUGS Les expressions d'évaluation de code, les expressions conditionnelles et les expressions indépendantes sont I. Ne les utilisez pas dans du code de production. Pas encore. =head1 VOIR AUSSI Ce document n'est qu'un simple tutoriel. Pour une documentation complète sur les expressions rationnelles en Perl, voir L. Pour plus d'informations sur l'opérateur de mise en correspondance C et l'opérateur de substitution C, voir L. Pour plus d'informations sur les opérations de découpage via C, voir L. Un très bon livre sur l'utilisation des expressions rationnelles, voir le livre I par Jeffrey Friedl (édité par O'Reilly, ISBN 1556592-257-3) (N.d.t: une traduction française existe.) =head1 AUTEURS ET COPYRIGHT Copyright (c) 2000 Mark Kvale Tous droits réservés. Ce document peut être distribué sous les mêmes conditions que Perl lui-même. =head2 Remerciements L'exemple des codons dans une chaîne d'ADN est librement inspiré de l'exemple de codes ZIP du chapitre 7 de I. L'auteur tient à remercier Jeff Pinyan, Andrew Johnson, Peter Haworth, Ronald J Kimball et Joe Smith pour leur aide et leurs commentaires. =head1 TRADUCTION =head2 Version Cette traduction française correspond à la version anglaise distribuée avec perl 5.10.0. Pour en savoir plus concernant ces traductions, consultez L. =head2 Traducteur Traduction initiale et mise à jour vers 5.10.0E: Paul Gaborit C. =head2 Relecture Jean-Louis Morel =cut