Documentation complet pour les débutants dans la programmation en Perl


Télécharger Documentation complet pour les débutants dans la programmation en Perl

★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Télécharger aussi :


Documentation complet pour les débutants dans la programmation en Perl

...

8.2.2      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 toute une classe de séquences.

L’un de ces concepts et celui de classe de caractères. 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 [...] avec l’ensemble de caractères placé à l’intérieur. Voici quelques exemples :

/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’ soit le premier caractère dans la classe, c’est le ’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 courante : une recherche de correspondance insensible à la casse. Perl fournit un moyen d’éviter tous ces crochets en ajoutant simplement un ’i’ après l’expression rationnelle. Donc /[oO][uU][iI]/; peut être réécrit en /oui/i;. Le ’i’ signifie insensible à la casse et est un exemple de modifica-teur 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 -]\ˆ$. ] est spécial car il indique la fin de la classe de caractères. $ est spécial car il indique une variable scalaire. \ est spécial car il est utilisé pour les séquences d’échappement comme précédemment. Voici comment les caractères spéciaux ]$\ sont utilisé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 [\$x], le backslash protège le signe dollar et donc la classe de caractères contient deux membres : $ et x. Dans [\\$x], le backslash est lui-même protégé et donc $x est traité comme une variable à laquelle on substitue sa valeur comme dans les chaînes entre guillemets.

Le caractère spécial ’-’ 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 [0123456789] et [abc...xyz] deviennent très simples : [0-9] et [a-z]. Quelques exemples :

8.2. PARTIE 1: LES BASES              perlretut

/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 ’-’ est le premier ou le dernier des caractères dans une classe, il est traité comme un caractère ordinaire ; [-ab], [ab-] et [a\-b] sont équivalents.

Le caractère spécial ˆ en première position d’une classe de caractères indique une classe de caractères inverse qui reconnaît n’importe quel caractère sauf ceux présents entre les crochets. Dans les deux cas, [...] et [ˆ...], il faut qu’un caractère soit reconnu sinon la reconnaissance échoue. Donc :

/[^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 [0-9] peut être ennuyeux à écrire plusieurs fois. Donc pour minimiser la frappe et rendre les expressions ration-nelles plus lisibles, Perl propose plusieurs abréviations pour les classes les plus communes :

– \d est un chiffre et représente [0-9]

– \s est un caractère d’espacement et représente [\ \t\r\n\f]

– \w est un caractère de mot (alphanumérique ou _) et représente [0-9a-zA-Z_]

– \D est la négation de \d; il représente n’importe quel caractère sauf un chiffre [ˆ0-9]

– \S est la négation de \s; il représente n’importe quel caractère qui ne soit pas d’espacement [ˆ\s]

– \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]

– Le point ’.’ reconnaît n’importe quel caractère sauf "\n"

Les abréviations \d\s\w\D\S\W peuvent être utilisées à l’intérieur ou à l’extérieur des classes de caractères. Voici quelques exemples :

/\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, \d et \w sont des ensembles de caractères, il est incorrect de penser que [ˆ\d\w] est équivalent à [\D\W]; en fait [ˆ\d\w] est la même chose que [ˆ\w], qui est la même chose que [\W]. C’est l’application des lois ensemblistes de De Morgan.

Une ancre pratique dans les expressions rationnelles de base est l’ancre de mot \b. Elle reconnaît la frontière entre un caractère mot et un caractère non-mot \w\W ou \W\w :

$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 ’.’ reconnaît tous les caractères sauf "\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 "\n" représente une ligne, nous aimons la considérer comme vide. Par conséquent :

""            =~ /^$/;               # est reconnue

"\n" =~ /^$/;     # est reconnue; "\n" est ignoré

 8.2. PARTIE 1: LES BASES             perlretut

""            =~ /./;   # 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; "\n" est ignoré              

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 ˆ et $ 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 //s et //m. //s et //m 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 rationnelle : 1) comment la classe de caractères ’.’ est définie et 2) où les ancres ˆ et $ peuvent s’ancrer. Voici les quatre combinaisons :

– pas de modificateurs (//) : comportement par défaut. ’.’ reconnaît n’importe quel caractère sauf "\n". ˆ correspond uniquement au début de la chaîne et $ correspond uniquement à la fin de la chaîne ou avant le passage à la ligne avant la fin de la chaîne.

– modificateur s (//s) : traite la chaîne comme une seule longue ligne. ’.’ reconnaît tous les caractères, même "\n". ˆ correspond uniquement au début de la chaîne et $ correspond uniquement à la fin de la chaîne ou avant le passage à la ligne avant la fin de la chaîne.

– modificateur m (//m) : traite la chaîne comme un ensemble de lignes. ’.’ reconnaît n’importe quel caractère sauf "\n". ˆ et $ peuvent correspondre au début et à la fin de n’importe quelle ligne dans la chaîne.

– les deux modificateurs s et m (//sm) : traite la chaîne comme une seule longue ligne mais détecte les lignes multiples. ’.’ reconnaît tous les caractères, même "\n". ˆ et $ peuvent correspondre au début et à la fin de n’importe quelle ligne dans la chaîne.

Voici des exemples d’utilisation de //s et //m :

$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 //s et //m sont occasionnellement très utiles. Si //m est utilisé, le début de la chaîne peut encore être reconnu par \A et la fin de la chaîne peut aussi être reconnue soit par l’ancre \Z (qui est reconnue à la fin de la chaîne ou juste avant le passage à la ligne, comme $) soit par l’ancre \z (qui n’est reconnue qu’à la fin de la chaîne) :

$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.

8.2. PARTIE 1: LES BASES              perlretut

8.2.3      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’alternative |. Pour reconnaître dog ou cat, nous formons l’expression rationnelle dog|cat. 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, dog. Si dog n’est pas reconnu, perl essaie ensuite la branche suivante, cat. Si cat 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 exemples :



"cats and dogs" =~ /cat|dog|bird/;        # reconnaît "cat"

"cats and dogs" =~ /dog|cat|bird/;        # reconnaît "cat"

Même si dog est la première branche de l’alternative dans le second exemple, cat 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.

8.2.4      Regroupement et reconnaissance hiérarchique

Les alternatives permettent à une expression rationnelle de choisir entre différentes branches mais elles restent non satis-faisantes 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 recon-naître housecat ou housekeeper. L’expression rationnelle housecat|housekeeper fonctionne mais est inefficace puisque nous avons du taper house deux fois. Il serait pratique d’avoir une partie de l’expression rationnelle qui soit constante, comme house, et une partie qui soit une alternative, comme cat|keeper.

Les meta-caractères de regroupement () 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 housecat|housekeeper en formant l’expression rationnelle house(cat|keeper). L’expression rationnelle house(cat|keeper) signifie cherche house suivi soit par cat soit par keeper. Quelques exemples :

/(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 groupe : à 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, "20" est reconnu par la deuxième branche de l’alternative mais il ne reste rien pour être reconnu par les deux chiffres suivants \d\d. Alors perl essaie la branche suivante qui est la branche vide et ça marche puisque "20" est composé de deux chiffres.

8.2. PARTIE 1: LES BASES              perlretut

Le processus d’essai d’une branche pour voir si elle convient puis d’une autre sinon est appelé retour arrière (backtracking en anglais). Les termes ’retour arrière’ viennent de l’idée que la reconnaissance d’une expression ra-tionnelle 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 rationnelle :

"abcde" =~ /(abd|abc)(df|d|de)/;

On démarre avec la première lettre de la chaîne ’a’.

On essaie la première branche de la première alternative, le groupe ’abd’.

On reconnaît un ’a’ suivi d’un ’b’. Jusqu’ici, ça va.

’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’.

On reconnaît un ’a’ suivi d’un ’b’ suivi d’un ’c’. Nous avons donc satisfait le premier regroupement. On positionne $1 à ’abc’.

On continue avec la seconde alternative et on choisit la première branche ’df’.

On reconnaît le ’d’.

’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’.

’d’ est reconnu. Le second regroupement est satisfait et on positionne $2 à ’d’.

Nous sommes à la fin de l’expression rationnelle. Nous sommes donc arrivés à destination ! Nous avons reconnu ’abcd’ dans la chaîne "abcde".

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 $string =˜ /(abd|abc)(df|d|de)/; .

Même avec tout ce boulot, les expressions rationnelles restent remarquablement rapides. Pour améliorer cela, durant la phase de compilation, perl compile les expressions rationnelles en une séquence compacte de opcodes qui peut parfois tenir dans le cache du processeur. Lorsque le code est exécuté, ces opcodes peuvent alors tourner à plein régime et chercher très rapidement.

8.2.5      Extraire ce qui est reconnu

Les meta-caractères de regroupement () servent aussi à une fonction totalement différente : 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 $1, $2, etc. Elles peuvent être utilisées comme des variables ordinaires :

8.2. PARTIE 1: LES BASES              perlretut

# 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, $time =˜ /(\d\d):(\d\d):(\d\d)/ retourne une valeur vraie ou fausse. Dans un contexte de liste, en revanche, il retourne la liste de valeurs reconnues ($1,$2,$3). Nous pouvons donc écrire du code plus compact :

# 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, $1 recevra le groupe dont la parenthèse ouvrante est la plus à gauche, $2 recevra le groupe dont la parenthèse ouvrante est la seconde la plus à gauche, etc. Par exemple, voici une expression rationnelle complexe et les variables correspondantes indiquées au-dessous :

/(ab(cd|ef)((gi)|j))/;

donc si cette expression rationnelle est reconnue, $2 devrait contenir soit ’cd’ soit ’ef’. De manière pratique, perl assigne à $+ la chaîne associée au plus haut numéro des $1, $2, ... qui a été le plus récemment affectée.

Associées avec les variables $1, $2, ..., on trouve les références arrières : \1, \2, ... Les références arrières sont sim-plement des variables de reconnaissance qui peuvent être utilisées à l’intérieur de l’expression rationnelle. C’est une fonctionnalité vraiment sympathique - ce qui est reconnu plus tard dans une expression rationnelle peut dépendre 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 deux :

/(\w\w\w)\s\1/;

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ées :

                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 \1 pour chercher une répétition. Bien que $1 et \1 représente la même chose, faites attention n’utiliser les variables $1, $2, ... qu’à l’extérieur d’une expression rationnelle et à n’utiliser les références arrières \1, \2, ... qu’à l’intérieur d’une expression rationnelle; ne pas y prêter attention peut amener à des résultats surprenants et/ou indéfinis.

En plus de ce qui a été reconnu, Perl 5.6.0 fournit aussi la position de ce qui a été reconnu grâce aux tableaux @- et @+. $-[0] est la position du début de l’ensemble de la reconnaissance et $+[0] est la position de la fin. De manière similaire, $-[n] est la position du début du groupe $n et $+[n] est la position de sa fin. Si $n est indéfini alors $-[n] et $+[n] le sont aussi. Donc le code :

$x = "Mmm...donut, thought Homer";

$x =~ /^(Mmm|Yech)\.\.\.(donut|peas)/; # reconnu foreach $expr (1..$#-) {

print "Match $expr: ’${$expr}’ at position ($-[$expr],$+[$expr])\n";

}

8.2. PARTIE 1: LES BASES              perlretut

affiche :

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 à $‘ la partie de la chaîne qui est avant ce qui a été reconnu, à $& la partie de la chaîne qui a été reconnue et à $’ la partie de la chaîne qui est après ce qui a été reconnu. Un exemple :

$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, $‘ = ” 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 $‘ et $’ ralentissent un peu le processus de reconnaissance des expressions rationnelles et que l’utilisation de $& 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 toutes les expressions rationnelles du programme. Donc si les performances font parties des buts dans votre projet, elles doivent être évitées. Préférez alors l’utilisation de @- et de @+ :

$‘ 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] )

8.2.6      Reconnaissances répétées

Les 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 de choses horribles comme \w\w\w\w|\w\w\w|\w\w|\w.

C’est exactement pour répondre à ce besoin que les meta-caractères quantificateurs ?, *, + et {} 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 suivante :

– a? = reconnaît ’a’ 1 ou 0 fois

– a* = reconnaît ’a’ 0 ou plusieurs fois

– a+ = reconnaît ’a’ 1 ou plusieurs fois

– a{n,m} = reconnaît au moins n ’a’, mais pas plus que m ’a’.

– a{n,} = reconnaît au moins n ’a’ ou plus

– a{n} = reconnaît exactement n ’a’

Voici quelques exemples :

/[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

8.2. PARTIE 1: LES BASES              perlretut

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 /a?.../, perl essayera d’abord de reconnaître l’expression rationnelle avec un a présent ; en cas d’échec, perl réessayera sans le a. Avec le quantificateur *, nous obtenons :

$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 cat présent dans la chaîne et se cale dessus. Considérons maintenant cette expression rationnelle :

$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 at dans cat et se serait arrêté là mais cela n’aurait pas donné la chaîne la plus longue possible pour le premier quantificateur .*. En fait, le premier quantificateur .* 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 at est mise en correspondance avec le dernier cat 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 .* qui consomme la plus grande partie de la chaîne alors que le second quantificateur

.* obtient la chaîne vide. Les quantificateurs qui consomment autant que possible sont qualifiés de maximaux ou de gourmands.

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 reconnue :

– Principe 0: Tout d’abord, une expression rationnelle sera toujours reconnue à la position la plus à gauche possible dans la chaîne.

– Principe 1: Dans un choix a|b|c..., c’est la branche la plus à gauche permettant une reconnaissance de l’ensemble de l’expression rationnelle qui sera choisie en premier.

– Principe 2: Les quantificateurs gourmands comme ?, *, + et {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.

– Principe 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 correspon-dance 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.

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 principes :

$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, ’T’. On pourrait penser que le e le plus à gauche de l’alternative devrait être reconnu mais le r 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 ’m’ de programming. m{1,2} est le premier quantificateur et donc il cherche la correspondance maximale mm.

8.2. PARTIE 1: LES BASES              perlretut

$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 .* consomme le plus de caractères possibles en ne laissant qu’un seul ’m’ pour le second quantificateur m{1,2}.

$x =~ /(.?)(m{1,2})(.*)$/;            # est reconnue avec

                $1 = ’a’

                $2 = ’mm’

                $3 = ’ing republic of Perl’

Ici, .? consomme le maximum de caractères (un caractère) à la position la plus à gauche possible dans la chaîne, le ’a’ dans programming, en laissant l’opportunité à m{1,2} de correspondre aux deux m. Finalement :

"aXXXb" =~ /(X*)/; # est reconnue avec $1 = ’’

parce qu’il peut reconnaître zéro ’X’ au tout début de la chaîne. Si vous voulez réellement reconnaître au moins un ’X’, utilisez X+ au lieu de X*.

Parfois la gourmandise n’est pas une bonne chose. Dans ce cas, nous aimerions des quantificateurs qui reconnaissent une partie minimale de la chaîne plutôt que maximale. Pour cela, Larry Wall a créé les quantificateurs minimaux ou quantificateurs sobres: ??,*?, +? et {}?. Ce sont les quantificateurs habituels auxquels on ajoute un ?. Ils ont la sémantique suivante :

– a?? = reconnaît un ’a’ 0 ou 1 fois. Essaie 0 d’abord puis 1 ensuite.

– a*? = reconnaît zéro ’a’ ou plus mais un minimum de fois.

– a+? = reconnaît un ’a’ ou plus mais un minimum de fois.

– a{n,m}? = reconnaît entre n et m ’a’ mais un minimum de fois.

– a{n,}? = reconnaît au moins n ’a’ mais un minimum de fois.

– a{n}? = reconnaît exactement n ’a’. C’est équivalent à a{n}. Ce n’est donc que pour rendre la notation cohérente.

Reprenons les exemples précédents mais avec des quantificateurs minimaux :

$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 ˆ et l’alternative est Th avec l’alternative e|r qui reconnaît e. 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 ’m’ de programming. À cette position, le sobre m{1,2}? reconnaît juste un ’m’. Ensuite, bien que le second quantificateur

.*? préfère reconnaître un minimum de caractères, il est contraint par l’ancre de fin de chaîne $ à 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 .*? soit mis en corres-pondance avec la chaîne vide puisqu’il n’est pas contraint à s’ancrer en début de chaîne par ˆ. 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 m et le troisième quantificateur reconnaît le reste de la chaîne.

8.2. PARTIE 1: LES BASES              perlretut

$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 ’a’, c’est donc ce qui est retenu. Le second quantificateur est gourmand donc il reconnaît mm 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 sobres :

– 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.

Comme avec les alternatives, les quantificateurs sont susceptibles de déclencher des retours arrière. Voici l’analyse pas à pas d’un exemple :

$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)

On démarre avec la première lettre de la chaîne ’t’.

Le premier quantificateur ’.*’ commence par reconnaître l’ensemble de la chaîne ’the cat in the hat’.

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.

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.

Maintenant nous pouvons reconnaître le ’a’ et le ’t’.

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.

C’est fini.

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 forme :

/(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 + et le * : une répétition avec b+ de longueur n, deux répétitions avec le premier b+ 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 toutes les possibilités avant de conclure à l’échec. Donc, soyez prudents lorsque vous imbriquez des *, des {n,m} et/ou des +. Le livre Mastering regular expressions par Jeffrey Friedl donne de très bonnes explications sur ce problème en particulier et sur tous les autres problèmes de performances.

8.2. PARTIE 1: LES BASES              perlretut

8.2.7      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 \d+ et le signe peut être reconnu par [+-]. Donc l’expression rationnelle pour les entiers est :

/[+-]?\d+/;         # reconnaît les entiers



230