précédentsommairesuivant

II. Séance de dissection

2. Composants de Perl

Où nous étudions les éléments de l'ensemble.

Dans les prochains chapitres, nous progresserons du particulier vers le général, approche ascendante, en commençant par décrire les composants élémentaires qui permettront d'aborder les structures plus élaborées, un peu à la manière dont les atomes forment les molécules. L'inconvénient est que vous n'en aurez pas nécessairement une image globale sans auparavant avoir été submergé par de nombreux détails, mais l'avantage est que vous comprendrez les exemples au fur et à mesure. (si vous préférez l'approche inverse, retournez le livre et lisez ce chapitre à l'envers).

Chaque chapitre est construit sur le précédent ce qui nécessite une lecture linéaire, (vous devrez donc faire attention si vous êtes du genre à sautiller d'une page à l'autre).

Vous êtes invité à utiliser les différentes annexes à la fin du livre. (Ce qui n'est pas du sautillement.) Chaque mot distingué par une police type se trouve au chapitre 29, Fonctions. Bien que nous avons essayé de ne pas privilégier un système d'exploitation, si vous êtes peu au courant de la terminologie Unix et si vous rencontrez un mot qui semble dire autre chose que ce que vous pensez, reportez-vous au glossaire. Si cela ne marche pas, essayez l'index.

2-1. Atomes

Le plus petit élément visible du langage est le caractère, celui que l'on peut visualiser dans un éditeur par exemple. Au départ, Perl ne faisait pas la distinction entre octets et caractères ASCII, mais pour des raisons d'internationalisation, il faut bien distinguer les deux.

Le code Perl peut être écrit exclusivement en ASCII, mais autorise aussi l'utilisation d'un codage sur 8 ou 16 bits, qu'il soit celui d'une langue ou bien de tout autre codage défini régulièrement, à condition de le faire dans des chaînes litérales uniquement. Bien sûr, Perl ne vérifiera pas le contenu du texte, mais saura simplement qu'un caractère est codé sur 16 bits.

Comme expliqué au chapitre 15, Unicode, Perl implémente Unicode.(35) C'est transparent pour l'utilisateur du langage qui peut en utiliser aussi bien dans des identificateurs (noms de variables, etc.) que dans des chaînes litérales. Pour Perl, tous les caractères ont la même taille de référence, par exemple 1, quelle que soit la représentation interne qu'il en fait ensuite. En principe, il code les caractères sous forme UTF-8 et donc un caractère peut avoir une forme interne sur 1, 2, 3... octets. (L'Unicode smiley U-263A est une séquence de 3 octets.)

Mais poursuivons l'analogie physique un peu plus avant avec les atomes. Les caractères sont atomiques de la même façon que les atomes codent les différents éléments. Un caractère est composé de bits et d'octets, mais si on le désintègre (dans un accélérateur de caractères, sans doute), il perd toutes ses propriétés propres. De la même manière que les neutrons composent l'atome d'Uranium U-238, les octets qui composent le caractère smiley U-263A sont un détail de son implémentation.

Il faut donc bien distinguer le mot « caractère » du mot « octet ». C'est pourquoi on peut le préciser à l'interpréteur avec la directive use bytes. (Voir le chapitre 31, Modules de pragmas). De toute manière, Perl saura par lui-même faire le codage sur 8 bits quand il le faudra.

Maintenant, passons dans la prochaine dimension.

2-2. Molécules

Perl est un langage informel, ce qui ne veut pas dire qu'il n'a pas de forme, mais plutôt dans le sens usuel du terme, c'est-à-dire un langage qu'on peut écrire avec des espaces, tabulations et caractères de nouvelle ligne partout où vous voulez sauf dans les unités syntaxiques.

Par définition, un symbole ne contient pas d'espace. Un symbole est une unité syntaxique avec un sens pour l'interpréteur, comme pour un mot dans une phrase, mais qui peut contenir d'autres caractères que des lettres tant que cette unité n'est pas rompue. (Ce sont de vraies molécules qui peuvent utiliser toutes sortes d'atomes. Par exemple, les nombres et les opérateurs mathématiques sont des symboles. Un identificateur est un symbole qui débute par une lettre ou un souligné et qui ne contient que des lettres, chiffres ou soulignés. Un symbole ne peut contenir d'espace puisque le caractère espace couperait le symbole en deux nouveaux symboles, tout comme un caractère espace, en français couperait un mot en deux nouveaux mots.(36)

Bien que le caractère espace soit nécessaire entre deux symboles, les espaces ne sont obligatoires qu'entre deux symboles qui, sinon, seraient pris pour un seul. À cet effet, tous les espaces sont équivalents. Un commentaire est compté comme un espace. Les sauts de ligne ne sont différents des espaces qu'entre des délimiteurs, dans certains formats et pour des formes orientées ligne de protection. Plus précisément le caractère de saut de ligne ne délimite pas une instruction comme en FORTRAN ou en Python. Les instructions en Perl se terminent par le caractère « ; » comme en C.

Les caractères d'espace Unicode sont autorisés dans un programme Perl Unicode moyennant certaines précautions. Si vous utilisez les caractères spéciaux Unicode séparateurs de paragraphe et de ligne, d'une manière différente que ne le fait votre éditeur de texte, les messages d'erreur seront plus difficile à interpréter. C'est mieux d'utiliser les caractères de saut de ligne classiques.

Les symboles sont détectés d'une manière large ; l'interpréteur Perl lit le symbole le plus long possible lors de la lecture du code. Si vous voulez qu'il distingue deux symboles, il suffit d'insérer un espace blanc entre eux. (La tendance est d'insérer plusieurs espaces pour rendre le code plus lisible.)

Un commentaire commence par le caractère # et s'étend jusqu'à la fin de la ligne. Un commentaire équivaut à un espace et joue le rôle de séparateur. Perl n'interprète pas une ligne placée en commentaire.(37)

Si une ligne, et c'est une autre originalité lexicale, commence par = à un endroit où une instruction serait légale, Perl ignore tout de cette ligne jusqu'à la prochaine contenant =cut. Le texte ignoré est supposé être du POD, ou Plain Old Documentation (des programmes Perl permettent de convertir ces commentaires au format des pages de manuel, ou en documents , HTML et bientôt XML). D'une manière complémentaire, l'analyseur lexical extrait le code Perl d'un module et ignore les balises pod, ce qui permet de conserver la documentation à l'intérieur des modules. Voir le chapitre 26, POD, pour les détails sur pod et la documentation multiligne.

Mais seul le premier # est nécessaire, les autres ne sont que de la décoration, bien que la plupart des codeurs l'utilisent pour faire des effets visuels comme avec le langage C ou ils font une utilisation abondante du caractère *.

En Perl, comme en chimie ou en linguistique, on construit des structures de plus en complexes à partir d'éléments simples. Par exemple, une instruction est une séquence de symboles sur le mode impératif. On peut combiner une série d'instructions pour former un bloc délimité par des accolades. Les blocs peuvent eux-mêmes constituer d'autres blocs. Certains blocs fonctionnels tels que les sous-programmes peuvent être combinés en modules eux-mêmes combinés en programmes, nous verrons tout ceci dans les prochains chapitres. Mais créons encore des symboles avec nos caractères.

2-3. Types internes

Avant de construire des types plus complexes, nous devons introduire quelques abstractions, plus précisément trois types de données.

Chaque langage dispose de ses propres types de données et à la différence des autres, Perl en possède peu ce qui réduit les confusions possibles. Prenons par exemple le langage C qui dispose des types suivants : char, short, int, long, long long, bool, wchar_t, size_t, off_t, regex_t, uid_t, u_longlong_t, pthread_key_t, fp_exception_field_type, et ainsi de suite. Ce sont juste les types entiers! Il y a ensuite les nombres flottants, les pointeurs et les chaînes.

Tous ces types compliqués n'en font qu'un en Perl : le scalaire. (Les types simples de données sont suffisants pour les besoins usuels, et il est possible de créer ses propres types en utilisant la programmation objet en Perl, voir le chapitre 12, Objets.) Les trois types de base en Perl sont : les scalaires, les tableaux de scalaires et les hachages de scalaires (connus aussi sous le nom de tableaux associatifs). On peut aussi les appeler structures de données.

Les scalaires constituent le type fondamental à partir duquel les structures plus complexes peuvent être construites. Un scalaire peut contenir une seule valeur simple, typiquement une chaîne ou un nombre. Les éléments de ce type simple peuvent être combinés à l'intérieur des deux autres types composés. Un tableau est une liste ordonnée de scalaires accessibles par un indice numérique (les indices démarrent à zéro). Contrairement aux autres langages, Perl traite les indices négatifs en comptant à partir de la fin du type indexé — aussi bien des chaînes que des listes. Un hachage est un ensemble non ordonné de paires clef/valeur accessibles par une clef de type chaîne utilisée pour accéder à la valeur scalaire associée. Les variables ont toujours l'un de ces trois types. (À part les variables, Perl comprend des bidules plus ou moins confidentiels tels que des handles de fichiers et de répertoires, des sous-programmes, des typeglobs, des formats et des tables de symboles.)

Laissons la théorie et passons à la pratique du langage pour en voir les possibilités. Nous allons donc écrire du code et pour cela vous présenter les termes constituant les expressions en Perl régies par des règles syntaxiques. Nous utilisons la terminologie terme quand nous parlerons des unités syntaxiques, un peu comme dans les équations mathématiques.

La fonction des termes en Perl est de fournir des valeurs pour des opérateurs tels l'addition ou la multiplication. Mais à la différence d'une équation, Perl interprète l'expression dans une action logique de la machine. Une des actions les plus courantes est la sauvegarde d'une valeur en mémoire :

 
Sélectionnez
$x = $y;

C'est un exemple de l'opérateur d'affectation (non pas l'opérateur de test d'égalité des valeurs numériques, ce qui s'écrit == en Perl). L'affectation prend la valeur de $y et la place dans la variable $x. Notez que le terme $x n'est pas utilisé pour sa valeur, mais pour sa fonction de mémorisation. (L'ancienne valeur de $x disparaît avec l'affectation.) Nous disons que $x est une lvalue, parce qu'elle est cible d'une affectation à gauche d'une expression. Et nous disons que $y est une rvalue car à droite dans ce type d'expression.

Il y a un troisième type de valeur appelée temporaire qu'il est nécessaire de comprendre. Considérons l'expression simple suivante :

 
Sélectionnez
$x=$y + 1;

Perl prend la rvalue $y et lui ajoute la rvalue 1, ce qui produit une valeur temporaire qui est ensuite affectée à la lvalue $x. Ces valeurs temporaires sont placées dans une structure interne appelée pile.(38)

Les termes d'une expression empilent des valeurs, alors que ses opérateurs (dont nous parlerons dans le prochain chapitre) dépilent des valeurs et peuvent aussi empiler des résultats pour le prochain opérateur. Dans la pile, une expression produit autant de valeurs qu'elle en consomme. Nous reviendrons sur les valeurs temporaires.

Certains termes sont exclusivement des rvalues comme 1, certains autres peuvent jouer les deux rôles, comme la variable de l'exemple précédent nous l'a montré. C'est ce que nous allons voir dans la prochaine section.

2-4. Variables

Il y existe trois types de variables correspondant aux trois types abstraits mentionnés plus haut. Chacun d'eux est préfixé par un caractère spécial.(39) Les variables scalaires sont toujours définies avec un caractère $ initial, même si elles font référence à des scalaires à l'intérieur d'un tableau ou d'un hachage. Ce caractère fonctionne un peu comme l'article défini singulier (le ou la). Nous avons donc :

Construction

Signification

$jours

Valeur scalaire simple $jours.

$jours[28]

29e élément du tableau @jours.

$jours{'Fev'}

Valeur « Fev » du hachage %jours.

Noter que vous pouvez utiliser le même nom pour $jours, @jours, et %jours, Perl ne les confondra pas.

Il existe d'autres caractères spéciaux qui s'appliquent sur les scalaires et sont utilisés dans des cas précis. Ils ressemblent à ceci :

Construction

Signification

${jours}

Identique à $jours en levant l'ambiguïté devant les caractères alphanumériques.

$Calendrier::jours

Une autre variable $jours, dans le paquetage Calendrier.

$#jours

Dernier index du tableau @jours.

$jours->[28]

29e élément du tableau pointé par la référence $jours.

$jours[0][2]

Tableau multidimensionnel.

$jours{2000}{'Fev'}

Hachage multidimensionnel.

$jours{2000,'Fev'}

Émulation de hachage multidimensionnel.

Les tableaux ou les tranches de tableaux (ainsi que les tranches de hachage) débutent par le caractère @, qui fonctionne un peu comme l'article défini pluriel (les) :

Construction

Signification

@jours

équivaut à ($jours[0], $jours[1],... $jours[n]).

@jours[3,4,5]

équivaut à ($jours[3], $jours[4], $jours[5]).

@jours[3..5]

équivaut à @jours[3,4,5].

@jours{'jan','fev'}

équivaut à ($jours{'jan'}, $jours{'fev'}).

équivaut à ($jours[0], $jours[1],... $jours[n]). équivaut à ($jours[3], $jours[4], $jours[5]). équivaut à @jours[3,4,5]. équivaut à ($jours{'jan'}, $jours{'fev'}).

Les hachages débutent par % :

Construction

Signification

%jours

@(jan => 31, fev => $bissextil e? 29: 28, ...).

Chacun de ces neuf exemples peut servir de lvalue, c'est-à-dire qu'ils spécifient une adresse à laquelle on peut, en autres choses, loger une valeur. Avec les tableaux, les hachages et les tranches de tableaux ou de hachages, la lvalue fournit un moyen simple d'affecter plusieurs valeurs d'un seul coup :

 
Sélectionnez
@days =1.. 7;

2-5. Noms

Nous avons parlé de variables pour mémoriser des valeurs, mais il faut aussi mémoriser les noms et définitions de ces variables. En théorie, cela s'appelle des espaces de nommage. Perl dispose de deux sortes d'espaces de nommages : la table des symboles et les portées lexicales.(40) Il peut y avoir un nombre quelconque de tables et de portées lexicales et l'intersection des deux ensembles est nulle, pour chaque instance de l'interpréteur Perl. Nous détaillerons chacun de ces types tout au long de cet ouvrage. Disons simplement pour le moment que les tables de symboles sont des hachages à portée globale qui contiennent la table des symboles des variables globales (y compris les hachages des autres tables de symboles). A contrario, les portées lexicales sont des espaces anonymes non inclus dans une table, mais liés à un bloc de code du programme courant. Ils contiennent des variables accessibles uniquement dans le bloc en cours. (C'est pourquoi on utilise le terme portée.) Le mot lexical veut dire relatif au bloc de texte courant (ce qui n'a rien à voir avec ce qu'un lexicographe entend par là. Ne vous fâchez pas.).

Dans chaque espace de nommage, global ou lexical, chaque type de variable dispose de son propre sous-espace de nommage déterminé par le caractère spécial affecté au type. Il est possible, sans risque de conflit, d'utiliser le même nom pour une variable scalaire, un tableau ou un hachage (ou encore, pour un handle de fichier, un nom de sous-programme, un label ou votre lama familier). Cela signifie que $machin et @machin sont deux variables différentes. Cela veut aussi dire que $machin[1] est un élément de @machin et non une partie de $machin. Bizarre, vous avez dit bizarre ? Mais c'est normal, bizarrement.

Pour l'appel d'un sous-programme le caractère & est facultatif. Généralement un nom de sous-programme n'est pas une lvalue, quoique dans les versions récentes de Perl un sous-programme peut s'évaluer à une lvalue de telle sorte que l'on peut lui affecter une valeur en retour.

Parfois on veut accéder aux variables d'un symbole donné, « sym » par exemple. Il suffit d'utiliser le caractère spécial * où l'astérisque indique tout type de variable (scalaire, tableau, hachage). On les appelle typeglobs ; ils ont plusieurs fonctions. Ils peuvent être utilisés comme lvalues. L'importation des modules se fait par l'affectation des typeglobs. Nous reviendrons sur ce thème plus tard.

Perl, comme tout langage informatique, dispose d'une liste de mots réservés qu'il reconnaît comme termes spéciaux. Cependant, les noms de variables commençant par un caractère spécial, il n'y a pas de confusion possible avec les premiers. D'autres types de noms ne commencent pas par ces caractères : les descripteurs de fichiers et les étiquettes. Avec ceux-ci attention à ne pas entrer en conflit avec des mots réservés. Nous vous recommandons pour ces types, d'utiliser des majuscules pour leurs noms. Par exemple, si vous écrivez open(LOG, logfile) plutôt que le regrettable open(log, "logfile"). Perl comprendra que vous ne parlez pas de la fonction log et des logarithmes,(41) et prévient les futurs conflits avec les nouvelles versions de Perl. Le nom des modules utilisateurs commence avec une majuscule alors que les modules pragmas prédéfinis sont écrits en minuscules. Quand nous aborderons la programmation orientée objet, vous verrez que les noms de classes sont en capitales pour la même raison.

Comme vous pouvez le déduire du précédent paragraphe, la casse est importante dans les identificateurs — TRUC, Truc, et truc sont trois noms différents en Perl. Un identificateur commence avec une lettre ou un souligné, il peut contenir des lettres et des chiffres, y compris les caractères Unicode, et peut avoir une longueur comprise entre 1 et 251 inclus. Les idéogrammes Unicode sont des lettres, mais nous vous déconseillons de les utiliser si vous ne savez pas les lire. Voir le chapitre 15.

Les noms qui suivent ces caractères spéciaux ne sont pas nécessairement des identificateurs. Ils peuvent commencer par un chiffre auquel cas l'identificateur ne peut contenir que des chiffres comme dans $123. Les noms qui commencent par tout autre caractère qu'une lettre, un chiffre ou souligné (comme $? ou $$), sont limités à ce caractère et sont prédéfinis en Perl. Par exemple $$ est l'ID du processus courant et $? est l'indicateur d'état retourné par le dernier processus fils créé.

Dans la version 5.6 de Perl est implémentée une syntaxe extensible pour les noms de variable interne. Toute variable de la forme ${^NAME} est une variable spéciale réservée par Perl. Tous ces noms spéciaux sont dans la table principale de symboles. Voir le chapitre 28, Noms spéciaux, pour les exemples.

On pourrait penser que noms et identificateurs désignent la même chose, mais lorsqu'on parle de nom, c'est du nom absolu qu'il s'agit, c'est-à-dire celui qui indique à quelle table de symboles il appartient. De tels noms sont formés par une série d'identificateurs séparés par le symbole :: :

 
Sélectionnez
$Doc::Aide::Perl::Classe::chameau

Ce qui fonctionne comme un nom absolu de fichier :

 
Sélectionnez
/Doc/Aide/Perl/Classe/chameau

En Perl, ce chemin absolu indique les tables de symboles imbriquées, et le dernier identificateur est le nom de la variable elle-même, contenu dans la dernière table imbriquée. Par exemple, pour la variable de l'exemple précédent, la table de symboles se nomme Doc::Aide::Perl::Classe, et le nom de la variable dans cette table est $chameau. (la valeur de cette variable est bien entendu « beige ».)

Une table de symboles en Perl se nomme aussi un paquetage. Donc ces variables sont aussi appelées variables de paquetage. Les variables de paquetage sont dites privées relativement à leur paquetage, mais sont globales dans le sens où les paquetages sont eux-mêmes globaux, c'est-à-dire qu'il suffit de nommer le paquetage pour y accéder, ce qui est difficile à faire par inadvertance. Par exemple, un programme qui référence $Doc::categorie demande la variable $categorie dans le paquetage Doc::, qui n'a rien à voir avec la variable $Livre::categorie. Voir le chapitre 10, Paquetages.

Les variables attachées à une portée lexicale ne sont pas contenues dans un paquetage, et ne contiennent pas la séquence ::. (Les variables à portée lexicale sont déclarées avec my.)

2-5-a. Recherche des noms

La question est donc comment Perl devine le chemin absolu d'un nom ? Comment trouve-t-il par exemple le nom de $categorie ? Voici les étapes successives suivies par l'interpréteur Perl dans la phase de lecture du code, pour résoudre les noms dans le contexte courant :

  1. Premièrement, Perl inspecte le bloc immédiatement contenant, pour une déclaration du type my (ou our) (Voir le chapitre 29, ainsi que la section Déclarations avec portée au chapitre 4, Instructions et déclarations). S'il trouve cette déclaration, alors la variable est à portée lexicale et n'existe dans aucun — elle existe uniquement dans le bloc courant. Une portée lexicale est anonyme et ne peut donc être atteinte hors du bloc la contenant.(42)
  2. S'il ne trouve pas, Perl cherche une variable à portée lexicale dans le bloc contenant le précédent et s'il la trouve, il la marque comme appartenant à ce bloc de niveau supérieur. À noter que dans ce cas, sa portée comprend le bloc de la première étape. Dans le cas inverse, il répète cette deuxième étape jusqu'à la sortie du bloc racine, le plus grand contenant les précédents.
  3. Quand il n'y a plus de blocs contenants, l'interpréteur examine l'unité de compilation entière, ce qui peut être le fichier entier ou la chaîne en cours de compilation dans le cas de l'expression eval CHAINE. Dans ce dernier cas, la portée lexicale est la chaîne elle-même et non un bloc entre accolades. Si Perl ne trouve pas la variable dans la portée lexicale de la chaîne, il considère qu'il n'y a plus de bloc et retourne à l'étape 2 en partant de la portée lexicale du bloc eval STRING.
  4. À ce point, Perl n'a pas encore trouvé de déclaration pour notre variable (ni my, ni our). Perl abandonne la recherche d'une variable lexicale, et suppose que la variable recherchée est une variable de paquetage. Le degré suivant de recherche est donc le paquetage. Si la directive strict est active, le compilateur provoque une erreur, (sauf si la variable est prédéfinie ou importée dans le paquetage courant), car il ne peut exister de variable globale sans qualifiant. Il cherche la déclaration package toujours dans la portée lexicale, et s'il la trouve, insère le nom du paquetage trouvé au début de la variable.
  5. S'il ne trouve pas de déclaration de paquetage dans la portée lexicale, Perl recherche la variable dans le paquetage main qui contient tous les autres. En l'absence de déclaration contraire, $categorie veut dire $::categorie, qui veut dire $main::categorie.(main étant un paquetage dans le paquetage de plus haut niveau, cela veut aussi dire $::main::categorie ou encore $main::main::categorie et $::main::main::categorie et ainsi de suite. On en verra toute l'utilité dans Tables de symboles au chapitre 10.)

Il y a plusieurs conséquences que nous devons souligner ici :

  • Le fichier étant la plus grande portée lexicale, une variable à portée lexicale ne peut être vue en dehors du fichier dans lequel elle est déclarée. Les portées de fichier ne sont pas imbriquées.
  • Tout code Perl compilé appartient à au moins une portée lexicale et exactement un paquetage. La portée obligatoire est le fichier de code lui-même. Les blocs contenant définissent les portées successives. Tout code Perl est compilé dans un paquetage exactement, et si la déclaration du paquetage a une portée lexicale, le paquetage n'en a pas, il est global.
  • Une variable sans qualifiant peut être cherchée dans différentes portées lexicales, mais dans un seul paquetage, qui est le paquetage actif (déterminé par la portée lexicale de la variable).
  • Un nom de variable ne peut avoir qu'une portée. Bien que deux portées sont actives au même moment (lexicale et paquetage), une variable ne peut exister que dans une seule à la fois.
  • Un nom de variable sans qualifiant ne peut exister que dans une place mémoire, soit dans la première portée lexicale, soit dans le paquetage courant, mais pas les deux.
  • La recherche s'arrête dès que la place mémoire est trouvée, et les autres places mémoire qui auraient été trouvées si la recherche s'était poursuivie, ces places mémoire donc sont néanmoins inaccessibles.
  • La place mémoire d'une variable typique peut être complètement déterminée à la compilation.

Maintenant on sait comment le compilateur Perl résoud les noms, mais parfois on ne connaît pas ce nom au moment de la compilation : par exemple dans le cas d'une indirection. Dans ce cas, Perl fournit un mécanisme qui permet de remplacer une variable par une expression qui retourne une référence à cette variable. Par exemple, au lieu de dire :

  • $categorie

vous diriez :

  • ${ une_expression() }

et si la fonction une_expression() retourne une référence à la variable $categorie (ou bien la chaîne "categorie"), cela fonctionne de la même manière. Par contre, si la fonction retourne $thesaurus ce sera cette variable à la place. Cette syntaxe est la plus générale (et la moins lisible) des formes d'indirection, mais nous verrons des variantes plus pratiques au chapitre 8, Références.

2-6. Valeurs scalaires

Qu'il soit référencé directement ou indirectement, dans une variable, un tableau ou bien une variable temporaire, un scalaire contient toujours une valeur simple qui peut être un nombre, une chaîne ou une référence à une autre donnée. Il peut aussi n'avoir aucune valeur auquel cas il est dit indéfini. Les scalaires sont sans type, même s'ils peuvent référencer toutes sortes de données : il n'est pas nécessaire de les déclarer.(43)

Perl mémorise les chaînes comme des séquences de caractères sans contrainte de longueur ni de contenu. Nul besoin de déclarer une taille à l'avance et tout caractère peut y être inclus, y compris le caractère nul. Les nombres sont représentés sous forme d'entiers signés si possible, ou bien sous forme de nombres à virgule flottante en double précision dans le format natif de la machine. Les valeurs flottantes ne sont pas infiniment précises, c'est pourquoi certaines comparaisons de la forme (10/3 == 1/3*10) échouent mystérieusement.

Perl convertit les scalaires en différents sous-types suivant les besoins ; le numérique peut être traité comme du caractère et inversement, Perl faisant Ce Qu'il Faut. Pour convertir du caractère en numérique, Perl utilise la fonction C atof(3). Pour passer du numérique à une chaîne de caractères, il fait l'équivalent de sprintf(3) avec le format "%.14g" sur la plupart des machines. La conversion d'une chaîne non numérique comme truc convertit le literal à la valeur 0 ; s'ils sont actifs, les avertissements sont affichés sinon rien. Voir le chapitre 5, pour des exemples permettant d'identifier le contenu d'une chaîne.

Alors que les chaînes de caractères et les numériques sont interchangeables dans à peu près tous les cas, les références sont d'un genre différent. Elles sont fortement typées, ce sont des pointeurs non convertibles (transtypables) comprenant des compteurs de références et des invocations de destructeurs internes. Ils sont utilisables pour créer des types de données complexes, incluant vos propres objets. Ceci dit, les références restent des scalaires, car ce qui importe n'est pas tant la complexité d'une structure que le fait de la considérer comme un simple scalaire.

Par non convertibles, nous voulons dire que l'on ne peut pas, par exemple, convertir une référence de tableau en une référence de hachage. Les références ne sont pas convertibles en un autre type pointeur. Cependant, en utilisant une référence en numérique ou en caractère, on obtient une valeur numérique ou une chaîne de caractères, ce qui permet de se souvenir du caractère unique d'une référence, même si le « référencement » de la valeur est perdu au cours de la copie depuis la vraie référence. On peut comparer différentes références ou tester si elles sont définies. Mais on ne peut en faire beaucoup plus puisqu'il n'existe pas de moyen de convertir du numérique ou du caractère en référence. En principe, si Perl n'oblige pas à faire de l'arithmétique de pointeurs — ou carrément l'interdit —, ce n'est pas un problème. Voir le chapitre 8 pour en connaître plus sur les références.

2-6-a. Littéraux numériques

Les littéraux numériques sont spécifiés par n'importe quel format courant(44) de virgule flottante ou d'entier :

 
Sélectionnez
$x = 12345;             # entier
$x = 12345.67;          # virgule flottante
$x = 6.02E23;           # notation scientifique
$x = 4_294_967_296;     # souligné pour la lisibilité
$x = 0377;              # octal
$x = 0xffff;            # hexadécimal
$x = 0b1100_0000;       # binaire

Comme Perl utilise la virgule comme séparateur de liste, elle ne peut servir comme séparateur des milliers pour les grands nombres. Pour améliorer leur lisibilité, Perl permet d'utiliser à la place le caractère souligné. Celui-ci ne fonctionne qu'avec des constantes numériques définies dans le programme, et non pour les chaînes de caractères traitées comme du numérique ou pour les données externes lues par ailleurs. De même, les caractères initiaux 0x en hexadécimal et 0 en octal marchent uniquement avec les constantes. La conversion automatique d'une chaîne en un nombre ne reconnaît pas ces préfixes — vous devez faire une conversion explicite(45) avec la fonction oct (qui marche aussi pour les données hexadécimales à condition d'indiquer 0x ou 0b au début).

2-6-b. Chaînes littérales

Les littéraux alphanumériques sont habituellement délimités par des apostrophes simples ou doubles. Ils fonctionnent un peu comme les quotes (les délimiteurs de protection) sous UNIX : les apostrophes doubles permettent une interpolation des antislashs et des variables, les apostrophes simples non. (\ et \\ permettent d'inclure un antislash dans une chaîne entre apostrophes simples). Si vous voulez inclure toute autre séquence telle que \n (saut de ligne), vous devez utiliser les apostrophes doubles. (Les séquences antislashs sont des séquences d'échappement, car elles permettent d'« échapper » temporairement à l'interprétation normale des caractères.)

Une chaîne entre apostrophes simples doit être séparée du mot précédent par un espace, car ce caractère est valide — mais archaïque — dans un identificateur. On utilise plutôt la séquence :: plus visuelle : $main'var et $main::var représentent la même variable, la deuxième étant beaucoup plus lisible.

Les chaînes entre guillemets permettent l'interpolation de différents types de caractères bien connus dans les autres langages. Ils sont listés au tableau 2-1.

Tableau 2-1. Caractères échappés avec antislash

Code

Signification

\n

Saut de ligne (généralement LF).

\r

Retour chariot (généralement CR).

\t

Tabulation horizontale.

\f

Saut de page.

\b

Retour arrière.

\a

Alerte (bip).

\e

Caractère ESC.

\033

ESC en octal.

\x7f

DEL en hexadécimal.

\cC

Control-C.

\x{263a}

Unicode (smiley).

\N{NAME}

Caractère nommé.

La notation \N{NOM} est utilisée avec la directive use charnames décrite au chapitre 31. Cette notation permet d'utiliser les noms symboliques comme \N{GREEK SMALL LETTER SIGMA}, \N{greek:Sigma}, ou \N{sigma} — suivant la directive utilisée. Voir aussi le chapitre 15.

Il existe de plus des séquences d'échappement pour modifier la casse des caractères qui suivent. Voir le tableau 2-2.

Tableau 2-2. Échappements de casse

Code

Signification

\u

Force le caractère suivant en majuscule (« titlecase » en Unicode).

\l

Force le caractère suivant en minuscule.

\U

Force tous les caractères suivants en majuscules.

\L

Force tous les caractères suivants en minuscules.

\Q

Préfixe avec antislash tous les caractères non alphanumériques.

\E

Fin de \U, \L, ou \Q.

Il est aussi possible d'insérer le caractère de saut de ligne dans une chaîne qui peut donc s'étendre sur plus d'une ligne. Ceci peut être utile, mais aussi dangereux si vous oubliez un guillemet, auquel cas l'erreur sera annoncée à la prochaine occurrence de guillemet qui peut se trouver beaucoup plus loin dans votre fichier source. Heureusement ceci provoque immédiatement une erreur de syntaxe sur la même ligne, et Perl est suffisamment futé pour vous avertir qu'il s'agit peut-être d'une chaîne qui n'est pas terminée au bon endroit, et de plus il vous indique la ligne où la chaîne a dû commencer.

Parallèlement aux séquences d'échappement ci-dessus, les chaînes entre doubles apostrophes permettent une interpolation des variables scalaires et des listes de valeurs. Cela signifie que les valeurs de certaines variables peuvent être directement insérées dans une chaîne. C'est en fait une forme pratique de concaténation de chaîne.(46) L'interpolation ne peut être effectuée que pour des variables scalaires, la totalité d'un tableau (mais pas d'un hachage), des éléments unitaires d'un tableau ou d'un hachage, ou enfin, des tranches (des sélections multiples d'éléments) de tableaux ou de hachages. En d'autres termes, seules les expressions commençant par $ ou @ peuvent être interpolées, car ce sont les deux caractères (avec l'antislash) que l'analyseur de chaîne recherche. À l'intérieur d'une chaîne, un caractère @ ne faisant pas partie de l'identifiant d'un tableau ou d'un hachage doit être inclus dans une séquence d'échappement avec un antislash (@), sous peine d'une erreur de compilation. Bien qu'un hachage complet spécifié par % ne puisse être interpolé dans une chaîne, un élément ou une sélection multiples d'éléments d'un hachage peuvent l'être, car ils commencent respectivement par les caractères $ et @.

Le bout de code suivant affiche "Le prix est de $100.".

 
Sélectionnez
$Prix = '$100';                  # non interpolé
print "Le prix est de $Prix.\n"; # interpolé

Comme dans certains shells, les accolades autour d'un identifiant permettent de le différencier des autres caractères alphanumériques : "How ${verb}able!". En fait un identifiant entre accolades est forcément interprété en chaîne, comme les chaînes clefs d'un hachage. Par exemple,

 
Sélectionnez
$jours{'fev'}

peut aussi être écrit :

 
Sélectionnez
$jours{fev}

et les apostrophes sont censées être automatiquement présentes. Mais toute chose plus complexe dans l'indice sera interprétée comme une expression.

et vous devriez les mettre entre guillemets simples :

En particulier, il est nécessaire d'utiliser les simples guillemets dans les tranches de tableau :

 
Sélectionnez
@jours{'Jan','Fev'}     # Ok.
@jours{"Jan","Fev"}     # Ok aussi.
@jours{ Jan,  Fev }     # erreur si la directive use strict est active

Exceptés les indices de tableaux interpolés ou de hachages, il n'existe pas de niveaux multiples d'interpolation. En particulier, contrairement aux attentes de nombreux programmeurs shell, les apostrophes inverses ne permettent pas d'interpoler à l'intérieur d'apostrophes doubles. De même, les apostrophes simples ne permettent pas d'empêcher l'évaluation de variables à l'intérieur d'apostrophes doubles. L'interpolation est très puissante, mais d'un contrôle strict en Perl. Elle ne se produit que dans les guillemets et certaines opérations que nous décrirons dans la prochaine section.

 
Sélectionnez
print "\n";             # Ok, imprime un saut de ligne.
print \n ;              # MAUVAIS, pas de contexte d'interpolation.
2-6-c. Choisissez vos délimiteurs

Alors que l'on ne considère souvent les délimiteurs que comme des constantes, ils fonctionnent plus comme des opérateurs avec Perl, permettant plusieurs formes d'interpolation et de correspondance de motifs. Perl fournit des délimiteurs spécifiques pour ces fonctions, mais offre également le moyen de choisir un délimiteur pour chacune. Au tableau 2-3, on peut utiliser tout caractère autre qu'alphanumérique ou d'espace comme délimiteur à la place de /. (Les caractères saut de ligne et d'espace ne sont plus admis dans les nouvelles versions de Perl.)

Tableau 2-3. Constructions avec guillemets

Habituelle

Générique

Signification

Interpolation

''

q//

chaîne littérale

non

""

qq//

chaîne littérale

oui

``

qx//

exécution de commande

oui

()

qw//

liste de mots

non

//

m//

recherche de motif

oui

s///

s///

substitution de motif

oui

y///

tr///

traduction

non

""

qr//

expression rationnelle

oui

Certains, ci-dessus, sont uniquement des formes de « sucre syntaxique » ; ils évitent d'alourdir une chaîne avec trop d'antislashs, en particulier dans les expressions régulières où slash et antislash se côtoient.

Si vous utilisez les guillemets simples comme délimiteurs, aucune interpolation de variable ne sera faite (même si le tableau ci-dessus indique « oui »). Si le délimiteur ouvrant est un crochet, une parenthèse, une accolade ou le signe inférieur, le délimiteur fermant doit lui correspondre (les occurrences de délimiteurs doivent correspondre par paires). Par exemple :

 
Sélectionnez
$simple = q!Je dis "Tu dis, 'Elle l'a dit.'"!;
$double = qq(On ne peut pas avoir une "bonne" $variable?);
$bout_de_code = q {
    if ($condition) {
        print "Pris!";
    }
};

Le dernier exemple montre qu'il est possible d'utiliser le caractère espace entre l'opérateur de protection et le délimiteur. Pour les constructions à deux éléments, comme s/// et tr///, si la première paire de délimiteurs est une accolade, la seconde partie peut en utiliser un autre : on peut écrire s<foo>(bar) ou tr(a-f)[A-F]. Les espaces sont autorisés entre les deux paires de délimiteurs, ainsi le dernier exemple peut s'écrire :

 
Sélectionnez
tr [a-z]
   [A-Z];

Les caractères espace sont toutefois interdits lorsque le délimiteur est #. q#foo# est lu comme 'foo', alors que q #foo# est lu comme l'opérateur q suivi d'un commentaire. Le délimiteur sera pris sur la ligne suivante. Les commentaires peuvent être placés au milieu d'une construction à deux éléments, ce qui permet d'écrire :

 
Sélectionnez
s {foo}   # Remplacer foo
  {bar};  #    par bar.

tr [a-f]  # Translittération de minuscule hexa  
   [A-F]; #       vers majuscule hexa
2-6-d. Ou n'en mettez pas du tout

Un mot qui n'a pas d'interprétation dans la grammaire sera traité comme s'il était dans une chaîne protégée. Ce sont les mots simples.(47) Comme pour les handles de fichier et les labels, un mot simple entièrement en minuscules risque d'entrer en conflit avec un futur mot réservé. Si vous utilisez l'option -w, Perl vous avertira de ce risque. Par exemple :

 
Sélectionnez
@jours = (Lun,Mar,Mer,Jeu,Ven);
print STDOUT Salut, ' ', monde, "\n";

stocke dans le tableau @jours les abréviations des jours de la semaine et affiche « Salut tout le monde » suivi d'un saut de ligne sur STDOUT. En ôtant le handle de fichier, Perl essaiera d'interpréter Salut comme un handle de fichier avec à la clef une erreur de syntaxe. C'est tellement source d'erreur que certains voudront déclarer hors la loi les mots simples. Les opérateurs de protection listés ci-avant ont d'autres formes y compris l'opérateur « quote words » qw// qui opère sur une liste de mots séparés par des espaces :

 
Sélectionnez
@jours = qw(Lun Mar Mer Jeu Ven);
print STDOUT "Salut, tout le monde\n";

Vous pouvez aussi bannir les mots simples de votre code. Si on écrit :

 
Sélectionnez
use strict 'subs';

alors tout mot simple qui n'est pas interprété comme un appel à un sous-programme produit à la place une erreur de compilation. Cette restriction dure jusqu'à la fin du bloc qui la contient. Un autre bloc peut l'annuler par

 
Sélectionnez
no strict 'subs';

Il faut remarquer que les identifiants dans des constructions telles que :

 
Sélectionnez
"${verb}able"
$jours{Fév}

ne sont pas considérés comme des mots simples, car ils sont autorisés par une règle explicite plutôt que par le fait de « ne pas avoir d'interprétation dans la grammaire ».

Un nom simple avec :: en fin, tel que main:: ou bien Chameau:: est toujours considéré comme un nom de paquetage. Perl transforme Chameau:: en chaîne « Chameau » au moment de la compilation, et ne sera pas rejeté par la directive use strict.

2-6-e. Interpolation des tableaux

Les variables tableau sont interpolées dans une chaîne entre doubles apostrophes en réunissant tous leurs éléments avec le délimiteur spécifié dans la variable $"(48) l'espace sera pris par défaut. Les exemples suivants sont équivalents :

 
Sélectionnez
$temp = join($",@ARGV);
print $temp;

print "@ARGV";

À l'intérieur des motifs de recherche (qui comprend aussi une interpolation identique à celle des doubles guillemets) il existe une ambiguïté déplaisante : /$machin[chose]/ est-il interprété comme /${machin}[chose]/ (où [chose] est une classe de caractères pour l'expression) ou comme /${machin[chose]}/ (où [chose] est l'indice du tableau @machin) ? Si @machin n'existe pas, c'est bien sûr une classe de caractères. Si @machin existe, Perl devinera [chose], presque toujours à bon escient.(49) S'il décrypte mal, ou si vous êtes paranoïaque, des accolades permettent de forcer l'interprétation correcte comme ci-dessus. Et même si vous êtes simplement prudent, ce n'est pas une mauvaise idée.

2-6-f. Documents « ici même »

Un format de séparation orienté ligne est basé sur la syntaxe des documents « ici même » (« here » documents) du shell(50) Après les caractères <<, on saisit une chaîne destinée à terminer la partie à délimiter, toutes les lignes suivant la ligne courante jusqu'à la chaîne de terminaison étant délimitées. La chaîne de terminaison peut être un identifiant (un mot) comme du texte délimité. Dans ce cas, le type de délimiteur utilisé détermine le traitement du texte, comme en délimitation standard. Un identifiant non délimité fonctionne comme des apostrophes doubles. Il ne doit pas y avoir d'espace entre les caractères << et l'identifiant (un espace est traité comme un identifiant nul, ce qui est valable, mais pas recommandé, et correspond avec la première ligne vide ; voir le premier exemple Hourra~! plus loin). La chaîne de terminaison doit apparaître en tant que telle (sans délimiteurs et non entourée d'espaces) sur la dernière ligne.

 
Sélectionnez
print <<EOF;        # même exemple que précédemment
Le prix est $Prix.
EOF

print <<"EOF";      # même exemple qu'au-dessus avec des apostrophes
                    # doubles explicites
Le prix est $Prix.
EOF

print <<'EOF';      # avec des apostrophes simples
Tout est possible (par exemple le passage d'un chameau par le chas
d'une aiguille), c'est vrai. Mais imaginez l'état du chameau,
déformé en un long fil sanglant de la tête aux pieds.
--C.S. Lewis
EOF

print << x 10;      # affiche la ligne suivante 10 fois
Les chameaux débarquent! Ho u rra! Hourra!

print <<"" x 10;    # La même chose, en mieux
Les chameaux débarquent! Ho u rra! Hourra!

print <<`EOC`;  # exécute les commandes
echo comment vas-tu
echo yau de poêle?
EOC

print <<"dromadaire", <<"camelide";     # on peut les empiler
Je dis bactriane.
dromadaire
Elle dit lama.
camelide

N'oubliez quand même pas de mettre un point virgule à la fin pour finir la phrase, car Perl ne sait pas que vous n'allez pas essayer de faire ceci :

 
Sélectionnez
print <<ABC
179231
ABC
+ 20;  # affiche 179251

Si vous voulez indenter vos documents « ici même » avec votre code, il faut enlever les espaces devant chaque ligne manuellement :

 
Sélectionnez
($quote = <<'QUOTE') =~ s/^\s+//gm;
    The Road goes ever on and on,
    down from the door where it began.
QUOTE

Il est aussi possible d'instancier un tableau avec les lignes d'un document « ici même » comme suit :

 
Sélectionnez
@sauces = <<fin_lignes =~ m/(\S.*\S)/g;
    tomate normale
    tomate épicée
    chili vert
    pesto
    vin blanc
fin_lignes
2-6-g. Litéral V-chaîne

Un litéral qui commence avec un v suivi d'un ou plusieurs entiers séparés par des points est traduit en chaîne dont chaque atome a pour valeur ordinale chaque entier.

 
Sélectionnez
$crlf = v13.10;    # valeur ASCII: retour chariot, saut de ligne

Le terme v-chaîne est une contraction de « vecteur-chaîne » ou « version de chaîne ». Il fournit une alternative plus cohérente pour construire une chaîne à partir des valeurs numériques de chaque caractère. Par exemple, il est plus facile d'écrire v1.20.300.4000 plutôt que :

 
Sélectionnez
"\x{1}\x{14}\x{12c}\x{fa0}"
pack("U*", 1, 20, 300, 4000)
chr(1) . chr(20) . chr(300) . chr(4000)

Si un tel littéral est composé de deux ou trois points (au moins trois entiers), le préfixe v peut être omis.

 
Sélectionnez
print v9786;              # affiche le caractère UTF-8 SMILEY, "\x{263a}"
print v102.111.111;       # affiche "foo"
print 102.111.111;        # idem

use 5.6.0;                # nécessite au moins cette version de Perl

$ipaddr = 204.148.40.9;   # l'addresse IP de oreilly.com

Les v-chaînes sont pratiques pour représenter les adresses IP ou les numéros de version. En particulier avec le codage de caractères sur plus d'un octet qui devient courant de nos jours, on peut comparer les numéros de version de toute taille avec les v-chaînes.

Les numéros de version et les adresses IP ne sont pas très lisibles sous la forme de v-chaînes. Pour obtenir une chaîne affichable, utilisez l'option v dans un masque avec printf, par exemple dans "%vd", tel que décrit au chapitre 29. Pour en savoir plus sur les chaînes Unicode, voir le chapitre 15, ainsi que la directive use bytes au chapitre 31 ; pour la comparaison des versions avec les opérateurs de comparaison de chaîne, voir $^V au chapitre 28 ; et pour la représentation des adresses IPv4, voir gethostbyaddr au chapitre 29.

2-6-h. Autres symboles littéraux

Tout symbole préfixé par deux caractères « souligné » et postfixé de même est réservé pour un usage spécial par Perl. Deux mots spéciaux, à savoir __LINE__ et __FILE__, représentent le numéro de la ligne courante et le fichier à ce stade du programme. Ils ne peuvent être utilisés que comme des mots séparés ; ils ne sont pas interpolés dans les chaînes. De la même manière, __PACKAGE__ est le nom du paquetage courant de compilation. S'il n'existe pas, ce qui est le cas lorsque la directive package; est vide, __PACKAGE__ vaut undef. Le symbole __END__, ou les caractères Control-D ou Control-Z, peuvent être utilisés pour indiquer la fin logique du script courant avant la fin physique du fichier. Tout caractère suivant est ignoré par le lecteur Perl, mais peut être lu via le descripteur spécial DATA.

__DATA__ fonctionne de la même façon que __END__, mais ouvre le handle de fichier DATA à l'intérieur de l'espace de nom du paquetage courant, ce qui fait que si vous insérez plusieurs fichiers par require, chacun peut disposer de son handle DATA personnel et l'ouvrir sans conflit avec les autres. Pour en savoir plus, voir DATA au chapitre 28.

2-7. Contexte

Jusqu'à présent, nous avons vu un certain nombre de termes pouvant produire des valeurs scalaires. Avant d'en voir davantage, nous devons aborder la notion de contexte.

2-7-a. Contexte scalaire et contexte de liste

Chaque opération(51) d'un script Perl est évaluée dans un contexte spécifique, et la façon dont l'opération se comportera peut dépendre des contraintes de ce contexte. Il existe deux contextes majeurs : les scalaires et les listes. Par exemple, l'affectation d'une variable scalaire évalue la partie droite dans un contexte scalaire :

 
Sélectionnez
$x        = fonction();  # contexte scalaire
$x[1]     = fonction();  # contexte scalaire
$x{"ray"} = fonction();  # contexte scalaire

Mais l'affectation d'un tableau ou d'un hachage (ou d'une tranche de ceux-ci) évalue la partie droite dans un contexte de liste. L'affectation d'une liste de scalaire se fera aussi par un contexte de liste pour la partie droite.

 
Sélectionnez
@x        = fonction();  # contexte de liste
@x[1]     = fonction();  # id
@x{"ray"} = fonction();  # id
%x        = fonction();  # id

L'affectation à une liste de scalaires fournit aussi un contexte de liste en rvalue, même s'il n'y a qu'un seul élément dans la liste produite :

 
Sélectionnez
($x,$y,$z) = fonction(); # contexte de liste
($x)       = fonction(); # id

Ces règles sont inchangées quand la variable est déclarée avec les termes my ou our, ainsi nous avons :

 
Sélectionnez
my $x     = fonction();  # contexte scalaire
my @x     = fonction();  # contexte de liste
my %x     = fonction();  # id
my ($x)   = fonction();  # id

Vous aurez beaucoup de problèmes jusqu'au jour où vous aurez compris la différence entre le contexte scalaire et le contexte de liste, car certains opérateurs (telle notre fonction imaginaire fonction()), connaissent leur contexte de retour et typent leur valeur de retour en conséquence. (Ce comportement sera toujours signalé quand c'est le cas.) En langage informatique, on dit que les opérateurs ont leur type de retour surchargé par le contexte d'affectation. Il s'agit de la surcharge la plus simple basée sur deux types simples, le scalaire et la liste.

Si certains opérateurs répondent suivant le contexte, c'est parce que quelque chose indique son type scalaire ou liste. L'affectation se comporte comme une fonction qui fournit à son opérande de droite le contexte. On a besoin de savoir quel opérateur fournit quel contexte pour ses opérandes. Toutes les fonctions avec un contexte de liste, ont le terme LISTE dans leur description. Celles qui ne l'ont pas ont un scalaire. C'est en général assez intuitif.(52) Au besoin, on peut forcer un contexte scalaire au milieu d'un contexte de LISTE en utilisant la pseudo-fonction scalar. (Perl ne fournit pas le moyen de forcer un contexte de liste en contexte scalaire, car partout où un contexte de liste est prévu, ceci est déjà indiqué par le contexte LISTE de certaines fonctions de contrôle.)

Le contexte scalaire peut ensuite être classé en contexte de chaînes, numériques ou contexte tolérant. À la différence de la distinction entre scalaire et liste que nous venons de faire, les opérations ne savent jamais quel type de contexte scalaire elles ont en entrée. Elles se contentent de renvoyer le type de valeur scalaire qu'elles désirent et laissent Perl traduire les numériques en chaîne dans un contexte de chaîne, et les chaînes en nombres dans un contexte numérique. Certains contextes scalaires se moquent de savoir si une chaîne ou un numérique est retourné, ce qui fait qu'aucune conversion ne sera effectuée. (Cela arrive, par exemple, lorsqu'on affecte une valeur à une autre variable. Celle-ci prend simplement le même sous-type que l'ancienne valeur.)

2-7-b. Contexte booléen

Le contexte booléen est un contexte scalaire particulier. Il correspond à l'endroit où une expression est évaluée pour savoir si elle est à vrai ou à faux. Parfois nous écrivons Vrai ou Faux à la place de la définition technique utilisée par Perl : une valeur scalaire est à vrai si ce n'est pas une chaîne nulle ou le nombre 0 (ou sa chaîne équivalente : "0"). Les références sont toujours vraies. Une référence est toujours vraie, car c'est une adresse physique jamais nulle. Une valeur indéfinie, appelée undef, est toujours fausse, car elle vaut, suivant le contexte, "" ou 0. (Les listes n'ont pas de valeur booléenne, car elles ne sont jamais produites dans un contexte scalaire !)

Puisque le contexte booléen est un contexte tolérant, il n'entraîne aucune conversion ; à noter que c'est un contexte scalaire, et tout opérande pour lequel c'est nécessaire sera converti en scalaire. Et pour tout opérande le nécessitant, le scalaire retourné dans un contexte scalaire est une valeur booléenne possible, ce qui signifie par exemple qu'un opérateur retournant une liste, peut être utilisé dans un test booléen : la fonction unlink qui opère dans un contexte de liste, peut utiliser un argument de type array :

 
Sélectionnez
unlink @liste_fichiers;  # détruit chaque fichier de la liste.

Maintenant, utilisons le tableau dans une condition (donc contexte booléen), alors le tableau retourne sa cardinalité, car il sait que c'est un contexte scalaire ; cette valeur est vraie tant qu'il reste des éléments dans le tableau. Supposons alors que nous voulons vérifier que les fichiers ont été supprimés correctement, on écrirait :

 
Sélectionnez
while (@liste_fichiers) {
    my $fichier = shift @liste_fichiers;
    unlink $fichier or warn "Ne peut pas supprimer $fichier: $!\n";
}

Ici @liste_fichier est évaluée dans le contexte booléen induit par la boucle while, de telle sorte que Perl évalue le tableau pour voir si sa valeur est vraie ou fausse. Cette valeur est vraie tant que la liste contient des éléments et devient fausse dès que le dernier élément est enlevé (par l'opérateur shift). Notez que la liste n'est pas évaluée dans un contexte scalaire. Nous indiquons au tableau qu'il est scalaire et lui demandons ce qu'il vaut dans ce contexte.

N'essayez pas d'utiliser defined @files pour ce faire. La fonction defined regarde si le scalaire est égal à undef, et un tableau n'est pas un scalaire. Un simple test booléen suffit dans ce cas.

2-7-c. Contexte vide

Un autre type particulier des contextes scalaires est le contexte vide. Non seulement il se moque de la valeur à retourner, mais en plus il ne veut même pas de valeur de retour. Du point de vue du fonctionnement des fonctions, il n'est pas différent des autres contextes scalaires, mais par le switch -w de la ligne de commande, le compilateur Perl prévient de l'utilisation d'une expression sans effet secondaire à un endroit qui ne veut pas d'une valeur, comme dans une phrase qui ne renvoie pas de valeur. Par exemple, si une chaîne est employée comme phrase :

 
Sélectionnez
"Camel Lot";

on peut obtenir un avertissement tel que :

 
Sélectionnez
Useless use of a constant in void context in monprog line 123;
2-7-d. Contexte interpolatif

Nous avons déjà mentionné que les apostrophes doubles autour d'une chaîne permettent une interpolation des variables et une interprétation des antislashs, mais le contexte interpolatif (souvent appelé « contexte double quotes ») ne s'applique pas qu'aux chaînes entre doubles apostrophes. Les autres constructions à doubles quotes concernent l'opérateur général apostrophe inverse qx//, l'opérateur de recherche de motif m// et l'opérateur de substitution s/// et l'opérateur d'expression régulière qr//. En fait, l'opérateur de substitution fait une interpolation de sa partie gauche avant de faire la recherche de motif, puis à chaque motif trouvé fait une interpolation de sa partie droite.

Le contexte interpolatif n'intervient qu'entre apostrophes, ou pour des choses qui fonctionnent de la même façon, et il n'est donc peut-être pas vraiment correct de l'appeler contexte au même titre que les contextes scalaires ou les contextes de liste (ou peut-être bien que si).

2-8. Valeur de liste et tableaux

Maintenant que nous avons parlé des contextes, nous pouvons parler des listes de valeurs et de leur comportement dans les différents contextes. Nous avons déjà vu les listes de littéraux, définies en séparant chaque valeur par une virgule (et en encadrant la liste par une paire de parenthèses). Comme il n'est pas gênant (presque jamais) d'ajouter des parenthèses supplémentaires, la forme syntaxique d'une liste s'écrit :

 
Sélectionnez
(LISTE)

Nous venons de voir que la forme LISTE indique un terme qui retourne une liste à son argument, mais une liste littérale simple est l'exception à cette règle en ce qu'elle fournit un contexte de liste uniquement quand la liste elle-même est dans un contexte de liste. La valeur d'une liste litérale dans un contexte de liste est simplement les valeurs de chaque élément séparées par une virgule dans l'ordre spécifié. Comme un terme dans une expression, une liste litérale empile simplement des valeurs temporaires sur la pile Perl, elles seront utilisées ensuite par l'opérateur qui attend cette liste.

Dans un contexte scalaire, la valeur d'une liste est la valeur de son dernier élément, comme avec l'opérateur virgule du C qui ignore toujours la valeur à gauche et renvoie celle de droite (en faisant référence à ce que nous disions plus haut, la partie à gauche de la virgule fournit un contexte vide). L'opérateur virgule étant associatif à gauche, une liste de valeurs séparées par des virgules fournit toujours le dernier élément, car la virgule oublie chaque valeur précédente. Par exemple :

 
Sélectionnez
@truc = ("un", "deux", "trois");

affecte toute la liste de valeurs au tableau @truc, mais :

 
Sélectionnez
$truc = ("un", "deux", "trois");

n'affecte que la valeur « trois » à la variable $truc. Comme pour le tableau @liste_fichiers, l'opérateur virgule sait s'il est dans un contexte scalaire ou de liste et adapte son comportement en conséquence.

C'est important de souligner qu'une liste est différente d'un tableau. Une variable tableau connaît aussi son contexte, et dans un contexte de liste retourne la liste de ses valeurs internes comme une liste littérale. Mais dans un contexte scalaire elle retourne simplement sa longueur. Le code suivant affecte la valeur 3 à la variable $truc :

 
Sélectionnez
@truc = ("un", "deux", "trois");
$truc = @truc;

Si vous pensiez obtenir la valeur « trois », vous avez généralisé un peu vite, car en fait Perl a détecté le contexte scalaire et n'a empilé que la longueur du tableau. Aucun terme ou opérateur n'empilera des valeurs de liste dans un contexte scalaire. Il empilera une valeur scalaire, qui ne sera certainement pas la dernière valeur de la liste qu'il retournerait dans un contexte de liste, valeur scalaire qui est ici la longueur du tableau. Il faut bien comprendre cet exemple (sinon, relisez le paragraphe, car c'est important).

Retournons aux vraies LISTE, celles qui induisent un contexte de liste. Jusqu'à présent nous avons soutenu que le contexte LISTE ne contient que des littéraux. Mais en fait, toute expression qui retourne une valeur peut être utilisée à l'intérieur d'une liste. Les valeurs ainsi utilisées peuvent être des valeurs scalaires ou des listes de valeurs. Le contexte LISTE fait automatiquement une interpolation des sous-listes. C'est pourquoi, lorsqu'un contexte LISTE est évalué, chaque élément de la liste est évalué dans ce contexte et la valeur de la liste résultante est interpolée dans LISTE comme si chaque élément individuel était un membre de LISTE. Les tableaux perdent ainsi leur identité dans LISTE. La liste :

 
Sélectionnez
(@machin,@chose,&sousProgramme)

contient tous les éléments de @machin, suivis de tous les éléments de @chose, suivis de tous les éléments renvoyés par le sous-programme sousProgramme lorsqu'il est appelé dans un contexte de liste. Notez que si l'un quelconque des éléments est nul, son interpolation ne compte pas. La liste nulle est représentée par (). Son interpolation dans une liste n'a pas d'effet. Ainsi ((),(),()) est équivalent à (). De même, l'interpolation d'un tableau sans élément donne, à ce stade, la même chose que s'il n'avait pas été interpolé.

Une conséquence est que l'on peut mettre une virgule optionnelle à la fin de toute liste. Ceci facilitera ultérieurement les ajouts d'éléments.

 
Sélectionnez
@nombres = (
    1,
    2,
    3,
);

Le mot qw, que nous avons mentionné plus tôt, permet aussi d'entrer une liste littérale. Il construit quelque chose d'équivalent à une chaîne entre apostrophes éclatée sur plusieurs lignes. Par exemple :

 
Sélectionnez
@machin = qw(
    pomme       banane      carambole
    orange      goyave      kumquat
    mandarine   nectarine   pêche
    poire       persimmon   prune
);

Remarquez que ces parenthèses ne sont pas ordinaires et fonctionnent comme des apostrophes. On aurait pu aussi mettre des signes inférieur-supérieur, des accolades ou des slashs (mais les parenthèses c'est joli).

Une liste de valeurs peut aussi être indicée comme un tableau standard. Vous devez alors mettre la liste entre parenthèses (des vraies) pour éviter toute ambiguïté. Si cela sert souvent pour obtenir une valeur du tableau, c'est aussi une tranche de la liste et la forme syntaxique s'écrit :

 
Sélectionnez
(LISTE)[LISTE]

Exemples :

 
Sélectionnez
# Stat renvoie une valeur liste.
$modification_time = (stat($fichier))[8];

# ERREUR DE SYNTAXE ICI.
$modification_time = stat($fichier)[8]; # OUPS, PAS DE PARENTHESES

# Trouver un chiffre hexa.
$chiffrehexa = ('a','b','c','d','e','f')[$chiffre-10];

# Un "opérateur virgule inverse".
return (pop(@machin),pop(@machin))[0];

# Affectation de plusieurs valeurs à l'aide d'une tranche.
($jour, $mois, $annee) = (localtime)[3,4,5];
2-8-a. Affectation de liste

Une liste est affectée si chacun de ses éléments est affecté :

 
Sélectionnez
($a, $b, $c) = (1, 2, 3);
($map{rouge}, $map{vert}, $map{bleu}) = (0xff0000, 0x00ff00, 0x0000ff);

Il est possible d'affecter à undef dans une liste. C'est utile pour ignorer certaines valeurs de retour d'une fonction :

 
Sélectionnez
($dev, $ino, undef, undef, $uid, $gid) = stat($fichier);

L'élément final d'une liste peut être un tableau ou un hachage :

 
Sélectionnez
($a, $b, @reste) = split;
my ($a, $b, %reste) = @arg_list;

Vous pouvez mettre un tableau ou un hachage dans la liste affectée en sachant que le premier de ce type rencontré absorbera les valeurs restantes et les variables restantes seront initialisées à undef. Ceci peut être utile dans une initialisation de type my ou local, où on veut que les tableaux soient vides.

Il est aussi possible d'affecter une liste vide :

 
Sélectionnez
() = fonction_bidon();

De cette façon, la fonction est appelée dans un contexte de liste, mais on laisse tomber les valeurs renvoyées. Si vous aviez fait l'appel sans affectation, la fonction aurait été évaluée dans un contexte vide, qui est un contexte scalaire, et se serait comportée complètement différemment.

L'affectation de liste dans un contexte scalaire retourne le nombre d'éléments que fournit la partie droite de l'affectation :

 
Sélectionnez
$x = ( ($machin,$truc) = (7,7,7) ); # $x contient 3, pas 2
$x = ( ($machin,$truc) = f() );     # $x contient le nombre d'éléments de f()
$x = ( () = f() );                  # idem

C'est pratique lorsqu'on veut faire l'affectation d'une liste dans un contexte booléen, car la plupart des fonctions liste retournent une liste nulle lorsqu'elles se terminent, valeur qui affectée à un scalaire produit la valeur 0 qui vaut faux dans ce contexte. Voici un exemple d'utilisation dans une boucle while :

 
Sélectionnez
while (($login, $password) = getpwent) {
    if (crypt($login, $password) eq $password) {
        print "$login a un mot de passe non sécurisé!\n";
    }
}
2-8-b. Longueur d'un tableau

Le nombre d'éléments du tableau @jours est donné par l'évaluation de @jours dans un contexte scalaire :

 
Sélectionnez
@jours + 0;     # force implicitement @jours dans un contexte scalaire
scalar(@jours)  # force explicitement @jours dans un contexte scalaire

Attention, cela ne fonctionne que pour les tableaux. Cela ne fonctionne pas pour les listes de valeurs en général. Une liste avec des virgules comme séparateur évaluée dans un contexte scalaire retournera la dernière valeur, comme l'opérateur virgule en C. Mais puisqu'il n'est presque jamais nécessaire de connaître la longueur d'une liste en Perl ce n'est pas un problème.

$#jours est très proche de l'évaluation scalaire de @jours. Cette dernière renvoie l'indice du dernier élément du tableau, ou un de moins que la longueur puisqu'il existe (habituellement) un élément d'indice zéro. L'affectation de $#jours change la longueur du tableau. Raccourcir un tableau de la sorte détruit les valeurs intermédiaires. Vous pouvez être plus efficace en surdimensionnant à l'avance un tableau destiné à s'agrandir (pour agrandir un tableau, on peut lui affecter un élément au-delà de sa fin). Un tableau peut être tronqué par l'affectation de la liste nulle (). Les deux instructions suivantes sont équivalentes :

 
Sélectionnez
@nimportequoi = ();
$#nimportequoi = -1;

Et celle qui suit est toujours vraie :

 
Sélectionnez
scalar(@nimportequoi) == $#nimportequoi + 1;

La troncation d'un tableau ne récupère pas la mémoire ainsi libérée. Il faut faire un undef(@nimportequoi) pour récupérer sa mémoire dans le processus courant. Vous ne pouvez vraisemblablement pas la retourner au système d'exploitation, car peu de systèmes en sont capables.

2-9. Hachages

Comme nous l'avons déjà dit, un hachage n'est qu'un type spécial de tableau dans lequel on retrouve les valeurs par une chaîne-clef au lieu d'un indice numérique. En fait, on définit des associations entre les clefs et les valeurs, et c'est pourquoi les hachages sont souvent appelés tableaux associatifs par les personnes assez courageuses pour taper sur un clavier.

Il n'existe pas vraiment de syntaxe particulière aux hachages en Perl, mais si une liste ordinaire est affectée à un hachage, chaque paire de valeurs de la liste sera prise comme une association clef/valeur.

 
Sélectionnez
%map = ('rouge',0xff000,'vert',0x00ff00,'bleu',0x0000ff);

Ce qui a le même effet que :

 
Sélectionnez
%map = ();               # d'abord initialiser le hachage
$map{rouge} = 0xff0000;
$map{vert}  = 0x00ff00;
$map{bleu}  = 0x0000ff;

Il est souvent plus lisible d'utiliser l'opérateur => entre la clef et sa valeur. L'opérateur => est synonyme de la virgule, mais il est plus clair visuellement et permet aussi une séparation d' identifiant à gauche (comme les identifiants entre accolades ci-dessus) ; l'initialisation des variables hachages devient plus simple :

 
Sélectionnez
%map = (
    rouge => 0xff0000,
    vert => 0x00fff0,
    bleu => 0x0000ff,
);

ou pour initialiser des références banalisées de hachage utilisées comme des enregistrements :

 
Sélectionnez
$rec = {
    NOM => 'John Smith'
    GRADE => 'Captain'
    MATRICULE => '951413',
};

ou pour utiliser des appels complexes de fonctions par un nom :

 
Sélectionnez
$field = $query->radio_group(
                    NOM => 'group_name',
                    VALEURS => ['eenie','meenie','minie'],
                    DEFAUT => 'meenie',
                    SAUTDELIGNE => 'true',
                    LABELS => %labels,
                );

Mais n'allons pas trop vite. Revenons à nos hachages.

Une variable hachage (%hachage) peut être utilisée dans un contexte de liste, toutes ses paires clef/valeurs étant interpolées dans la liste. Mais ce n'est pas parce que le hachage a été initialisé dans un certain ordre que les valeurs sont retournées dans le même. Les hachages sont implémentés au niveau interne en utilisant des tables de hachage pour une recherche rapide, ce qui signifie que l'ordre dans lequel les données sont stockées dépend du type de fonction de hachage utilisée pour déterminer la place de la paire clef/valeur. Ainsi, les entrées sont apparemment retournées dans un ordre quelconque (au niveau d'une paire clef/valeur, les deux éléments sont bien sûr rendus dans le bon ordre). Pour des exemples de réordonnancement en sortie, voir la fonction keys au chapitre 29.

Si un hachage est évalué dans un contexte scalaire, une valeur vraie est renvoyée si et seulement si le hachage contient des paires clef/valeur. (S'il existe plusieurs paires, la valeur renvoyée est une chaîne contenant le nombre de cellules utilisées (buckets) et le nombre de cellules allouées, séparés par un slash. Ceci n'est utile que pour savoir si l'algorithme résident de Perl traite correctement ou non l'ensemble de données. Par exemple, on entre 10 000 éléments dans un hachage, mais l'évaluation de %HASH dans un contexte scalaire révèle « 1/8 », cela signifie que seulement une des 8 cellules a été touchée, et qu'elle contient probablement les 10 000 éléments. Ce n'est pas censé se produire.

Pour trouver le nombre de clefs dans un hachage, utilisez la fonction keys dans un contexte scalaire : scalar(keys(%HASH)).

Il est possible d'émuler un tableau multidimensionnel en spécifiant plus d'une clef dans les accolades séparées par une virgule. Les clefs listées sont concaténées ensemble séparées par le caractère $; ($SUBSCRIPT_SEPARATOR) qui a la valeur chr(28) par défaut. La clef résultante devient une clef du hachage. Ces deux lignes font la même chose :

 
Sélectionnez
$habitants{ $pays, $département } = $résultat_recensement;
$habitants{ join $; =>; $pays, $département } = $résultat_recensement;

Cette fonctionnalité fut à l'origine implémentée pour le traducteur a2p (awk vers perl). Aujourd'hui, vous utiliseriez plus justement un tableau multidimensionnel comme il est décrit au chapitre 9, Structures de données. Un usage encore pratique de cet ancien style est la liaison des hachages aux fichiers DBM (voir DB_File au chapitre 32, Modules standards), qui n'implémentent pas les clefs multidimensionnelles.

Ne confondez pas l'émulation multidimensionnelle des hachages avec les tranches de tableaux. L'un est une valeur scalaire, l'autre représente une liste :

 
Sélectionnez
$hachage{ $x, $y, $z }      # une valeur simple
@hachage{ $x, $y, $z }      # une tranche de trois valeurs

2-10. Typeglobs et handles de fichiers

Perl utilise un type spécial appelé typeglob qui contient la table des types Perl pour ce symbole. (La table des symboles *foo contient les valeurs de $foo, @foo, %foo, &foo et d'autres interprétations de foo.) Le préfixe de typage d'un typeglob est *, car il représente tous les types.

Les typeglobs (ou leurs références) sont toujours utilisés pour passer ou stocker des handles de fichier. Pour sauvegarder un handle de fichier il faut taper :

 
Sélectionnez
$fh = *STDOUT;

ou comme une vraie référence, de cette manière :

 
Sélectionnez
$fh = \*STDOUT;

C'est aussi le moyen de créer un handle de fichier local. Par exemple :

 
Sélectionnez
sub newopen {
    my $path = shift;
    local *FH;      # et non my ou our!
    open (FH, $path) || return undef;
    return *FH;     # et non \*FH!
}
$fh = newopen('/etc./passwd');

Voir la fonction open pour générer d'autres handles de fichiers.

Cependant, la principale utilisation des typeglobs est actuellement de servir d'alias entre deux entrées symboliques. C'est comme un surnom. Si on écrit

*machin = $truc; tout ce qui s'appelle « machin », est synonyme de tout ce qui s'appelle « truc ». L'alias peut porter sur un seul type des variables du nom en affectant une référence :

 
Sélectionnez
*machin = $truc;

$machin devient alias de $truc, mais @machin n'est pas synonyme de @truc, ni %machin de %truc. Tout ceci ne concerne que les variables globales, c'est-à-dire définies dans un paquetage ; les variables lexicales ne peuvent être accédées à travers la table des symboles. Le mécanisme d'import/export est entièrement basé sur ce principe, l'alias ne présuppose en effet aucune appartenance à un module spécifique. Cette instruction :

 
Sélectionnez
local *Ici::bleu = \$Ailleurs::vert;

fait de $Ici::bleu un alias pour $Ailleurs::vert, mais ne fait pas de @Ici::bleu un alias pour @Ailleurs::vert, ni %Ici::bleu un alias pour %Ailleurs;::vert. Heureusement, toutes ces manipulations compliquées de typeglobs sont transparentes la plupart du temps. Voir le chapitre 8, et Tables de symboles au chapitre 10 et le chapitre 11, Modules, pour un développement sur ces sujets.

2-11. Opérateurs d'entrée

Nous allons maintenant présenter plusieurs opérateurs d'entrée qui sont analysés comme des termes. En fait, on les appelle parfois des pseudo-littéraux, car ils agissent, dans bien des cas, comme des chaînes protégées (les opérateurs de sortie comme print fonctionnent comme des opérateurs de liste et sont traités au chapitre 29.)

2-11-a. Opérateur d'exécution de commande (Backtick)

Premièrement il y a l'opérateur de ligne de commande, aussi appelé opérateur backtick, et qui ressemble à ceci :

 
Sélectionnez
$info = `finger $user`;

Une chaîne protégée par des apostrophes inverses procède tout d'abord à une interpolation des variables comme dans une chaîne entre apostrophes doubles. Le résultat est alors interprété comme une commande par le shell, et la sortie de la commande devient la valeur de l'identifiant (ceci fonctionne comme certains opérateurs similaires des shells UNIX). Dans un contexte scalaire, le résultat est une chaîne simple contenant toutes les sorties de la commande. Dans un contexte de liste, une liste de valeurs est renvoyée, une pour chaque ligne sortie par la commande. (On peut utiliser $/ pour avoir un autre caractère de terminaison de ligne.)

La commande est interprétée chaque fois que l'identifiant est évalué. La valeur numérique du statut de la commande est stockée dans $? (voir le chapitre 28 pour l'interprétation de $?, connu aussi sous le nom de $CHILD_ERROR). À la différence de csh, aucune transformation n'est faite sur les données renvoyées ; les sauts de ligne restent des sauts de ligne. À l'inverse de tous les shells, les apostrophes simples n'empêchent pas l'interprétation des noms de variables. Pour passer un $ au shell il faut le cacher par un antislash. Le $user de notre exemple ci-dessus est interpolé par Perl, mais non par le shell (la commande lançant un processus shell, consultez le chapitre 23, Sécurité, pour les problèmes de sécurité).

La forme généralisée pour les apostrophes inverses est qx// (pour quoted execution, exécution protégée), mais l'opérateur fonctionne exactement de la même façon que les apostrophes inverses ordinaires. Il suffit de choisir ses caractères de protection. Comme avec les pseudo-fonctions de protection, si vous choisissez un simple guillemet comme délimiteur, la chaîne de commande n'est pas interpolée :

 
Sélectionnez
$perl_info = qx(ps $$);         # Variable $$ de Perl
$shell_info = qx'ps $$';        # Variable $$ du shell
2-11-b. Opérateur de lecture de ligne (Angle)

L'opérateur de lecture de ligne est l'opérateur le plus utilisé, aussi connu sous le nom d'opérateur inférieur-supérieur, ou fonction readline. L'évaluation d'un handle de fichier entre inférieur-supérieur (par exemple <STDIN>) donne la ligne suivante du fichier associé. Le saut de ligne est inclus, donc en accord avec les critères de véracité de Perl, chaque ligne lue a une valeur vrai, et lorsque le programme atteint la fin du fichier, l'opérateur angle retourne undef, c'est-à-dire faux. On affecte d'habitude la valeur lue à une variable, mais il existe un cas où une affectation automatique se produit. Si, et seulement si, l'opérateur de lecture de ligne est la seule chose présente dans le test de boucle d'un while, la valeur est automatiquement affectée à la variable spéciale $_. La valeur affectée est alors testée pour savoir si elle est définie (cela peut vous paraître idiot, mais vous utiliserez cette construction dans presque tous vos scripts Perl). Bref, les lignes suivantes sont toutes équivalentes :

 
Sélectionnez
while (defined($_ = <STDIN>)) { print $_; }     # méthode longue
while ($_ = <STDIN>) { print; }                 # utilise $_ explicitement
while (<STDIN>) { print; }                      # méthode courte
for (;<STDIN>;) { print; }                      # boucle while déguisée
print $_ while defined($_ = <STDIN>);           # instruction modifiée longue
print while $_ = <STDIN>;                       # utilise $_
print while <STDIN>;                            # instruction modifiée courte

Souvenez-vous que cette astuce requiert une boucle while. Si vous utilisez l'opérateur ailleurs, vous devez affecter le résultat explicitement pour garder la valeur.

 
Sélectionnez
while (<FH1> && <FH2>) {...}            # mauvais : perd les deux entrées
if (<STDIN>) { print; }                 # mauvais, affiche l'ancienne
                                        # valeur de $_
if ($_ = <STDIN>) { print; }            # ne teste pas si $_ est défini
if (defined($_ = <STDIN>)) { print; }   # meilleur

Quand la variable $_ est implicitement affectée dans une boucle, c'est la variable globale dont il s'agit. Vous pouvez protéger la valeur de $_ de la manière suivante :

 
Sélectionnez
while (local $_ = <STDIN>) { print; }   # utilise local $_

L'ancienne valeur est rétablie à la sortie de la boucle. $_ peut toujours être accédée depuis la boucle. Pour éviter toute confusion, il est préférable d'utiliser une variable lexicale :

 
Sélectionnez
while (my $ligne = <STDIN>) { print $ligne; } # variable privée

(Ces deux boucles while testent si l'affectation est defined, car my et local ne changent pas son comportement habituel.) Les handles de fichiers STDIN, STDOUT et STDERR sont prédéfinis et ouverts. D'autres handles peuvent être créés avec les fonctions open ou sysopen. Voir la documentation de ces fonctions au chapitre 29 pour les détails.

Dans la boucle while ci-dessus, la ligne d'entrée est évaluée dans un contexte scalaire, chaque valeur est retournée séparément. Si on l'utilise dans un contexte de liste, une liste de toutes les lignes d'entrée restantes est retournée, chaque ligne étant un élément de liste. Un important espace de données peut ainsi être créé, et il faut donc l'utiliser avec précaution :

 
Sélectionnez
$une_ligne = <MYFILE># Donne la première ligne.
@toutes_lignes = <MYFILE>;  # Donne le reste des lignes.

Il n'existe pas de magie particulière pour le while associé à la forme liste de l'opérateur d'entrée, car la condition d'une boucle while est toujours un contexte scalaire (comme toutes les conditions).

L'utilisation du handle de fichier nul à l'intérieur de l'opérateur inférieur-supérieur (ou opérateur angle) est particulière et peut être utilisée pour émuler la ligne de commande de programmes standards d'UNIX tels que sed et awk. Quand on lit les lignes depuis <>, toutes les lignes de tous les fichiers mentionnés sur la ligne de commande sont renvoyées. Si aucun fichier n'était spécifié, c'est l'entrée standard qui est renvoyée à la place et le programme peut ainsi être facilement inséré entre des processus pour former un pipe.

Voici comment cela fonctionne : la première fois que <> est évalué, le tableau @ARGV est contrôlé, et s'il est nul, $ARGV[0] est mis à« -», qui donne l'entrée standard quand on l'ouvre. Le tableau @ARGV est traité comme une liste de noms de fichiers. La boucle

 
Sélectionnez
while (<>) {
    ...          # code pour chacune des lignes
}

est équivalente au pseudo-code Perl suivant :

 
Sélectionnez
while (@ARGV and $ARGV[0] =~ /^-/) {
    $_=shift;
    last if /^--$/;
    if (/^-D(.*)/) { $debug = $1 }
    if (/^-V/) { $verbose++ }
    ...          # autres alternatives
}
while (<>) {
    ...          # du code dans chaque ligne
}

à part une meilleure lisibilité, le résultat reste identique. Le tableau @ARGV est décalé et le nom du fichier courant est stocké dans la variable $ARGV. Le handle de fichier ARGV est aussi utilisé en interne ; <> est simplement un synonyme de <ARGV>, qui est magique (le pseudo-code ci-dessus ne fonctionne pas parce qu'il traite <ARGV> comme non-magique).

Il est possible de modifier @ARGV avant l'instruction <> du moment que le tableau est construit avec les noms de fichier que vous attendez. Le nom de fichier « -» qui représente l'entrée standard, peut être ouvert avec la fonction open, ainsi que des flux plus ésotériques comme « gzip -dc < file.gz| »). Les numéros de ligne ($.) s'incrémentent comme s'il n'existe qu'un fichier en entrée. (Mais observez l'exemple ci-dessous sur eof pour voir comment on réinitialise les numéros de ligne à chaque fichier.)

Pour affecter @ARGV à une liste de fichiers, c'est direct :

 
Sélectionnez
# par défaut: le fichier README si aucun argument n'est donné
@ARGV = ("README") unless @ARGV;

Pour passer des options dans un script, on peut utiliser un des modules Getopt ou mettre une boucle initiale telle que :

 
Sélectionnez
while ($_ = $ARGV[0], /^-/) {
    shift;
    last if /^--$/;
    if (/^-D(.*)/) { $debug = $1 }
    if (/^-v/) { $verbose++ }
    ...         # autres options
}
while (<>) {
    ...         # code pour chacune des lignes
}

Le symbole <> ne renvoie faux qu'une seule fois. En cas d'appel ultérieur, une autre liste @ARGV est supposée être traitée, et si @ARGV n'est pas initialisé les lectures se font depuis STDIN.

Si la chaîne entre l'opérateur angle est une variable (par exemple, <$machin>), alors celle-ci contient le nom du fichier à lire ou une référence à celui-ci. Par exemple :

 
Sélectionnez
$fh = \*STDIN;
$ligne = <$fh>;

ou :

 
Sélectionnez
open($fh, "<donnees.txt>");
$line = "<$fh>";
2-11-c. Opérateur de globalisation des noms de fichier

Vous vous souciez peut-être de ce qui peut arriver à un opérateur de lecture de ligne si vous mettez quelque chose de bizarre entre l'inférieur et le supérieur. Il est alors transformé en un opérateur différent. Si la chaîne à l'intérieur de l'opérateur angle est différente du nom d'un handle de fichier ou d'une variable scalaire (même s'il agit d'espace), elle sera interprétée comme étant un motif de fichier à « globaliser ».(53) Le motif de recherche s'applique aux fichiers du répertoire courant (ou dans le répertoire spécifié dans le motif glob lui-même), et les fichiers correspondants sont retournés par l'opérateur. Comme pour les lectures de ligne, les noms sont renvoyés un à un dans un contexte scalaire, ou tous à la fois dans un contexte de liste. Ce dernier usage est en fait prédominant. Il est courant de voir :

 
Sélectionnez
my @fichiers = <*.xml>;

Comme pour d'autres types de pseudo-constantes, un premier niveau d'interpolation est effectué, mais on ne peut pas écrire <$machin>, car il s'agit d'un handle de fichier indirect, comme nous l'avons déjà vu. Dans d'anciennes versions de Perl, les programmeurs inséraient des accolades pour forcer une interprétation de nom de fichier glob : <${machin}>. De nos jours, on considère qu'il est plus propre d'appeler la fonction interne directement par glob($machin), ce qui aurait dû être le cas dès l'origine. On écrirait donc :

 
Sélectionnez
@fichiers = glob("*.xml");

si vous préférez.

Que l'on utilise la fonction glob ou sa forme ancienne avec l'opérateur d'angle, l'opérateur de globalisation fonctionne de la même façon dans une boucle que l'opérateur while en affectant le résultat à $_. (Ce fut la première raison de surcharger cet opérateur.) Par exemple, si vous vouliez changer les autorisations de fichiers de code C, vous écririez :

 
Sélectionnez
while (glob "*.c") {
    chmod 0644, $_;
}

ce qui est équivalent à :

 
Sélectionnez
while (<*.c>) {
    chmod 0644, $_;
}

La fonction glob était implémentée comme une commande shell dans les anciennes versions de Perl (aussi dans les anciennes versions d'Unix), était plus coûteuse en ressources et ne fonctionnait pas de la même façon sur tous les systèmes. C'est aujourd'hui une fonction prédéfinie plus fiable et plus rapide. Voyez la description du module File::Glob au chapitre 32 pour modifier son comportement par défaut pour, par exemple, traiter les espaces dans ces arguments, l'expansion de certains caractères (tilde ou accolade), l'insensibilité à la casse ou bien le classement des valeurs retournées, entre autres choses.

Bien sûr, la façon la plus courte et la moins lisible pour faire la commande chmod ci-dessus, est d'utiliser la globalisation comme un opérateur sur liste :

 
Sélectionnez
chmod 0644, <*.c>;

Un glob n'évalue son argument que quand il démarre une nouvelle liste. Toutes les valeurs doivent être lues avant son exécution. Dans un contexte de liste, ce n'est pas très important, puisqu'on les a toutes automatiquement. Mais dans un contexte scalaire, l'opérateur renvoie la prochaine valeur à chaque appel, ou une valeur fausse si on arrive au bout. Attention, faux n'est retourné qu'une seule fois. Ainsi, si l'on attend une valeur unique pour un glob, il vaut mieux écrire :

 
Sélectionnez
($fichier) = <blurch*>; # contexte de liste

au lieu de :

 
Sélectionnez
$fichier = <blurch*>;   # contexte scalaire

car la première forme avale tous les noms de fichier correspondants et réinitialise l'opérateur, alors que ce que renvoie la seconde alterne entre un nom de fichier et faux.

Pour interpoler des variables, il faut absolument utiliser l'opérateur glob, car l'ancienne syntaxe peut être confondue avec la notation indirecte des handles de fichiers. Mais à ce niveau, il devient évident que la frontière entre les termes et les opérateurs est un peu floue.

 
Sélectionnez
@files = <$dir/*.[ch]>;         # à éviter
@files = glob("$dir/*.[ch]");   # appelle glob en tant que fonction
@files = glob $un_motif;        # appelle glob en tant qu'opérateur

Nous ne mettons pas les parenthèses dans le second exemple pour montrer que glob peut être utilisé comme un opérateur unaire, c'est-à-dire un opérateur préfixe avec un seul argument. L'opérateur glob est un exemple d'opérateur unaire défini, qui n'est qu'un des types d'opérateurs dont nous parlerons dans le prochain chapitre. Ensuite nous parlerons des opérations de recherche de motif, qui ressemblent elles aussi à des termes, mais fonctionnent comme des opérateurs.

3. Opérateurs unaires et binaires

Dans le chapitre précédent, nous avons parlé des différents types de termes que vous pouvez utiliser dans une expression. Mais pour être honnête, les termes isolés sont un peu ennuyeux. Beaucoup de termes sont de joyeux fêtards ; ils aiment communiquer les uns avec les autres. Un terme typique ressent une forte envie de s'identifier avec d'autres termes, ou de les influencer de différentes manières. Cependant, il existe de nombreuses sortes d'interactions sociales et de nombreux niveaux d'implication. En Perl, ces relations sont exprimées à l'aide d'opérateurs.

Il faut bien que la sociologie serve à quelque chose.

D'un point de vue mathématique, les opérateurs sont des fonctions ordinaires avec une syntaxe spéciale. D'un point de vue linguistique, les opérateurs sont juste des verbes du troisième groupe. Mais comme tout linguiste vous le dira, les verbes du troisième groupe sont généralement ceux que l'on utilise le plus souvent. C'est important du point de vue de la théorie de l'information, car les verbes du troisième groupe sont généralement plus courts et plus efficaces tant pour la production que pour la reconnaissance.

D'un point de vue pratique, les opérateurs sont maniables.

On distingue plusieurs variétés d'opérateurs, en fonction de leur arité (le nombre d'opérandes qu'ils prennent), leur précédence (leur capacité à prendre leurs opérandes aux opérateurs voisins), et leur associativité (agissent-ils de gauche à droite ou de droite à gauche quand ils sont associés à des opérateurs de même précédence).

Les opérateurs Perl se présentent sous trois arités : unaire, binaire ou ternaire. Les opérateurs unaires sont toujours des opérateurs préfixes (exceptés les opérateurs de post-incrémentation et de post-décrémentation).(54) Tous les autres sont des opérateurs infixes — à moins que vous ne comptiez les opérateurs de liste, qui peuvent précéder autant d'arguments que vous voulez. Cependant la plupart des gens préfèrent voir les opérateurs de liste comme des fonctions normales autour desquelles on peut oublier de mettre des parenthèses. Voici quelques exemples :

 
Sélectionnez
! $x                # un opérateur unaire
$x * $y             # un opérateur binaire
$x ? $y : $z        # un opérateur ternaire
print $x, $y, $z    # un opérateur de liste

La précédence d'un opérateur détermine sa force d'attraction. Les opérateurs de plus haute précédence capturent les arguments qui les entourent avant les opérateurs de moindre précédence. L'exemple typique sort tout droit des mathématiques élémentaires, où la multiplication est prioritaire sur l'addition :

 
Sélectionnez
2+3 * 4             # vaut 14, et non 20

L'ordre dans lequel sont exécutés deux opérateurs de même précédence dépend de leur associativité. Les règles suivent en partie les conventions des mathématiques :

 
Sélectionnez
2 * 3 * 4           # signifie (2 * 3) * 4, associatif à gauche
2 ** 3 ** 4         # signifie 2 ** (3 ** 4), associatif à droite
2 != 3 != 4         # illégal, car non associatif

Le tableau 3-1 liste l'associativité et l'arité des opérateurs Perl par ordre de précédence décroissante.

Tableau 3-1. Précédence des opérateurs

Associativité

Arité

Classe de précédence

Non associatifs

0

Termes et opérateurs de liste (vers la gauche)

Gauche

2

->

Non associatifs

1

++ --

Droite

2

**

Droite

1

! ~ \+ unaire et - unaire

Gauche

2

=~ !~

Gauche

2

* / % x

Gauche

2

+ - .

Gauche

2

<< >>

Droite

0,1

Opérateurs unaires nommés

Non associatifs

2

< > <= >= lt gt le ge

Non associatifs

2

== != <=> eq ne cmp

Gauche

2

&

Gauche

2

| ^

Gauche

2

&&

Gauche

2

||

Non associatifs

2

.....

Droite

3

?:

Droite

2

= += -= *= et suivants

Gauche

2

, =>

Droite

0+

Opérateurs de liste (vers la droite)

Droite

1

not

Gauche

2

and

Gauche

2

or xor

Vous pourriez penser qu'il y a bien trop de niveaux de précédence à se souvenir. Et bien vous avez raison. Heureusement, deux choses jouent en votre faveur. D'abord les niveaux de précédence tels qu'ils sont définis sont en général assez intuitifs, en supposant que vous n'êtes pas psychotique. Ensuite, si vous êtes seulement névrosé, vous pouvez toujours ajouter des parenthèses supplémentaires pour calmer votre angoisse.

Notez également que tous les opérateurs empruntés à C conservent les mêmes relations de précédence entre eux, même quand les règles de précédence de C sont légèrement biscornues. (Cela rend Perl d'autant plus facile à apprendre pour les personnes qui connaissent C ou C++. Peut-être même pour celles qui connaissent Java.)

Les sections qui suivent couvrent ces opérateurs dans leur ordre de précédence. À de très rares exceptions, ils opèrent tous uniquement sur des valeurs scalaires et non sur des listes. Nous indiquerons les exceptions quand elles se présenteront.

Bien que les références soient des valeurs scalaires, utiliser la plupart de ces opérateurs sur des références n'a pas beaucoup de sens, car la valeur numérique d'une référence n'a de signification que dans les profondeurs de Perl. Néanmoins, si une référence pointe sur un objet d'une classe qui autorise la surcharge d'opérateurs, vous pouvez utiliser ces opérateurs sur cet objet ; si la classe a défini une surcharge pour tel ou tel opérateur, cela décrit comment l'objet sera traité par cet opérateur. C'est ainsi par exemple que les nombres complexes sont implémentés en Perl. Pour plus d'informations sur la surcharge d'opérateurs, voir le chapitre 13, Surcharge.

3-1. Termes et opérateurs de listes (vers la gauche)

En Perl, tout terme est de précédence maximale. Les termes comprennent les variables, les apostrophes et les opérateurs de type quote, la plupart des expressions entre parenthèses, crochets ou accolades, et toute fonction dont les arguments sont entre parenthèses. En fait, si on regarde les choses ainsi, il n'y a pas vraiment de fonctions, mais seulement des opérateurs de liste et des opérateurs unaires qui se comportent comme des fonctions parce que vous avez mis des parenthèses autour de leurs arguments. Tout cela n'empêche pas le chapitre 29 de s'appeler Fonctions.

Maintenant, lisez attentivement. Voici quelques règles très importantes qui simplifient grandement les choses, mais qui peuvent à l'occasion produire des résultats contraires à l'intuition pour les imprudents. Si un opérateur de liste (comme print) ou un opérateur unaire nommé (comme chdir) est suivi d'une parenthèse ouvrante comme token suivant (sans tenir compte des blancs), l'opérateur et ses arguments entre parenthèses ont la précédence la plus haute, comme s'il s'agissait d'un appel de fonction normal. La règle est la suivante : si cela ressemble à un appel de fonction, alors c'est un appel de fonction. Vous pouvez le faire ressembler à une non-fonction en préfixant les parenthèses avec un plus unaire, qui ne fait absolument rien sémantiquement parlant ; il ne convertit même pas les arguments en numérique.

Par exemple, puisque || a une précédence plus faible que chdir, nous aurons :

 
Sélectionnez
chdir $toto || die;         # (chdir $toto) || die
chdir($toto) || die;        # (chdir $toto) || die
chdir ($toto) || die;       # (chdir $toto) || die
chdir +($toto) || die;      # (chdir $toto) || die

mais comme * a une précédence plus grande que chdir, nous aurons :

 
Sélectionnez
chdir $toto * 20;           # chdir ($toto * 20)
chdir($toto) * 20;          # (chdir $toto) * 20
chdir ($toto) * 20;         # (chdir $toto) * 20
chdir +($toto) * 20;        # chdir ($toto * 20)

De même pour n'importe quel opérateur numérique qui est également un opérateur unaire nommé, comme rand :

 
Sélectionnez
rand 10 * 20;               # rand (10 * 20)
rand(10) * 20;              # (rand 10) * 20
rand (10) * 20;             # (rand 10) * 20
rand +(10) * 20;            # rand (10 * 20)

En l'absence de parenthèses, la précédence d'opérateurs de liste comme print, sort, ou chmod est soit très haute soit très basse selon que vous regardez à gauche ou à droite de l'opérateur (c'est le sens du « vers la gauche » dans le titre de cette section). Par exemple, dans :

 
Sélectionnez
@tab = (1, 3, sort 4, 2);
print @tab;         # imprime 1324

Les virgules à droite de sort sont évaluées avant le sort, mais les virgules à sa gauche sont évaluées après. En d'autres termes, un opérateur de liste tend à avaler tous les arguments qui le suivent, puis à se comporter comme un simple terme pour l'expression qui le précède. Il vous faut encore être prudent avec les parenthèses :

 
Sélectionnez
# Ces arguments sont évalués avant de faire le print :
print($toto, exit);     # Évidemment pas ce que vous voulez.
print $toto, exit;      # Ici non plus.

# Ces lignes font le print avant d'évaluer l'exit :
(print $toto), exit;    # C'est ce que vous voulez.
print($toto), exit;     # Ici aussi.
print ($toto), exit;    # Et même ceci

Le cas le plus simple pour se faire piéger, c'est quand vous utilisez des parenthèses pour grouper des arguments mathématiques, en oubliant que les parenthèses servent aussi à regrouper les arguments de fonctions :

 
Sélectionnez
print ($toto & 255) + 1, "\n";   # affiche ($toto & 255)

Cela ne fait probablement pas ce à quoi vous vous attendiez au premier coup d'œil. Heureusement, les erreurs de cette nature provoquent des avertissements comme « Useless use of addition in a void context » quand les avertissements sont activés (avec l'option de ligne de commande -w).

Les constructions do {} et eval {} sont aussi analysées comme des termes, de même que les appels de sous-programmes et de méthodes, les créateurs de tableaux et de hachages anonymes, [] et {}, et le créateur de sous-programme anonymes sub {}.

3-2. L'opérateur flèche

Tout comme en C et en C++, l'opérateur binaire -> est un opérateur infixe de déréférencement. Si à droite on trouve un indice de tableau entre [...], un indice de hachage entre {...} ou une liste d'arguments de sous-programme entre (...), alors la partie à gauche de la f lèche doit être respectivement une référence (symbolique ou en dur) de tableau, de hachage ou de sous-programme. Dans un contexte de lvalue (où l'on peut affecter une valeur), si la partie à gauche n'est pas une référence, elle doit être capable de contenir une référence, auquel cas cette référence sera autovivifiée pour vous. Pour en savoir plus à ce sujet (et pour quelques avertissements sur l'autovivification accidentelle), voir le chapitre 8, Références.

 
Sélectionnez
$aref->[42]               # un déréférencement de tableau
$href->{"corned beef"}    # un déréférencement de hachage
$sref->(1,2,3)            # un déréférencement de sous-programme

Sinon, c'est une sorte d'appel de méthode. La partie à droite doit être un nom de méthode (ou une simple variable scalaire contenant le nom de la méthode) et la partie à gauche doit s'évaluer soit comme un objet (une référence consacrée), soit comme un nom de classe (c'est-à-dire un nom de paquetage) :

 
Sélectionnez
$yogi = Ours->new("Yogi");    # un appel de méthode de classe
$yogi->pique($piquenique);    # un appel de méthode sur une instance

Le nom de méthode peut être qualifié avec un nom de paquetage pour indiquer dans quelle classe commencer à chercher la méthode, ou avec le nom de paquetage spécial SUPER::, pour indiquer que la recherche doit commencer dans la classe parente. Voir le chapitre 12, Objets.

3-3. Auto-incrémentation et autodécrémentation

Les opérateurs ++ et -- fonctionnent comme en C. C'est-à-dire que, placés avant une variable, ils l'incrémentent ou la décrémentent avant d'en renvoyer la valeur. Quand ils sont placés après, ils l'incrémentent ou la décrémentent après en avoir renvoyé la valeur. Par exemple, $A++(55) incrémente la valeur de la variable $A et en retourne la valeur avant de réaliser l'incrémentation. De même, --$b{(/(\w+)/)[0]} décrémente l'élément du hachage %b indexé par le premier « mot » de la variable de recherche par défaut ($_) et renvoie la valeur après la décrémentation.(56)

L'opérateur d'auto-incrémentation comporte en plus un peu de magie. Si vous incrémentez une variable qui est numérique, ou qui a été utilisée à un moment donné dans un contexte numérique, vous avez une incrémentation normale. Si au contraire la variable n'a été utilisée que dans des contextes de chaîne depuis qu'elle a été affectée, que sa valeur est non nulle et correspond au motif /^[a-zA-Z]*[0-9]*$/, l'incrémentation est faite sur la chaîne, en préservant chaque caractère dans son domaine, avec une retenue :

 
Sélectionnez
print ++($toto = '99');     # prints '100'
print ++($toto = 'a9');     # prints 'b0'
print ++($toto = 'Az');     # prints 'Ba'
print ++($toto = 'zz');     # prints 'aaa'

À l'heure où nous écrivons ces lignes, l'auto-incrémentation magique n'a pas été étendue aux lettres et chiffres Unicode, mais pourrait l'être dans le futur.

L'opérateur d'autodécrémentation n'est, lui, pas magique, et il n'est pas prévu que cela change.

3-4. Exponentiation

L'opérateur ** binaire est l'opérateur d'exponentiation. Notez qu'il lie encore plus fortement que le moins unaire ; c'est pourquoi -2**4 vaut -(2**4), et non (-2)**4. L'opérateur est implémenté grâce à la fonction pow(3) de C, qui fonctionne en interne avec des nombres en virgule flottante. Les calculs sont faits grâce à des logarithmes, ce qui signifie qu'il fonctionne avec des puissances fractionnaires, mais que vous obtiendrez parfois des résultats moins précis qu'avec une simple multiplication.

3-5. Opérateurs unaires idéographiques

La plupart des opérateurs portent simplement des noms (voir Opérateurs unaires nommés et de test de fichier plus loin dans ce chapitre), mais certains opérateurs sont jugés suffisamment importants pour mériter leur propre représentation symbolique spéciale. Ces opérateurs semblent tous avoir un rapport avec la négation. Plaignez-vous auprès des mathématiciens.

Le ! unaire réalise une négation logique, c'est-à-dire un « non ». Voyez not pour une version de moindre précédence du non logique. La valeur d'un opérande nié est vraie (1) si l'opérande est faux (0 numérique, chaîne "0", chaîne vide ou valeur indéfinie) et fausse ("") si l'opérande est vrai.

Le - unaire effectue une négation arithmétique si son opérande est numérique. Si l'opérande est un identificateur, une chaîne composée d'un signe moins concaténé à l'identificateur est renvoyée. Sinon, si la chaîne commence avec un plus ou un moins, une chaîne commençant avec le signe opposé est renvoyée. Un des effets de ces règles est que -motsimple est équivalent à "-motsimple". C'est particulièrement utile aux programmeurs Tk.

Le ~ unaire réalise une négation sur les bits, c'est-à-dire un complément à 1. Par définition, ce n'est pas vraiment portable quand c'est limité par la taille du mot-machine de votre ordinateur. Par exemple, sur une machine 32 bits ~123 vaut 4294967172, tandis que sur une machine 64 bits, cela vaut 18446744073709551492. Mais vous le saviez déjà.

Ce que vous ne saviez peut-être pas, c'est que si l'argument de ~ est une chaîne au lieu d'un nombre, une chaîne de même longueur est renvoyée avec tous ses bits complémentés. C'est une manière rapide de basculer un grand nombre de bits d'un coup, et de façon portable puisque cela ne dépend pas de la taille de votre mot-machine. Plus tard nous verrons aussi les opérateurs logiques sur les bits, qui ont eux des variantes pour les chaînes.

Le + n'a aucun effet sémantique, même sur les chaînes. Son utilité est surtout syntaxique, pour séparer le nom d'une fonction d'une expression entre parenthèses qui serait sinon interprétée comme la liste complète des arguments de la fonction. (Voir les exemples dans la section Termes et opérateurs de listes (vers la gauche).) Si vous y réfléchissez d'une manière un peu détournée, le + inverse l'effet qu'ont les parenthèses de changer les opérateurs préfixes en fonctions.

Le \ crée une référence sur ce qui le suit. Utilisé sur une liste, il crée une liste de références. Voir la section L'opérateur antislash au chapitre 8 pour plus de détails. Attention à ne pas confondre ce comportement avec celui de l'antislash dans une chaîne, bien que les deux formes comportent la notion vaguement négative de protection de leurs arguments contre l'interprétation. Cette ressemblance n'est pas purement fortuite.

3-6. Opérateurs de liaison

Le =~ binaire lie une expression scalaire à un opérateur de recherche de motif, de substitution ou de translittération (négligemment appelée traduction). Ces opérations chercheraient ou modifieraient sinon la chaîne contenue dans $_ (la variable par défaut). La chaîne à lier est placée à gauche, tandis que l'opérateur lui-même est placé à droite. La valeur de retour indique le succès ou l'échec de l'opération effectuée à droite, puisque l'opérateur de lien ne fait rien par lui-même.

Si l'argument de droite est une expression plutôt qu'une recherche de motif, une substitution ou une translittération, il sera interprété comme une recherche de motif à l'exécution. C'est-à-dire que $_ =~ $pat est équivalent à $_ =~ /$pat/. C'est moins efficace qu'une recherche explicite, puisque le motif doit être vérifié et potentiellement recompilé à chaque fois que l'expression est évaluée. Vous pouvez éviter cette recompilation en précompilant le motif original avec l'opérateur qr// de citation d'expression régulière (« quote regex » en anglais).

Le !~ binaire est identique à =~ sauf que la valeur de retour est inversée logiquement. Les expressions suivantes sont fonctionnellement équivalentes :

 
Sélectionnez
$chaine !~ /pattern/ not
$chaine =~ /pattern/

Nous avons dit que la valeur de retour indique le succès, mais il existe plusieurs sortes de succès. La substitution renvoie le nombre de substitutions réussies, comme la translittération. (En fait, la translittération sert souvent à compter les caractères.) Puisque n'importe quel résultat non nul est vrai, tout fonctionne. La forme de valeur vraie la plus spectaculaire est une valeur de liste : en contexte de liste, les recherches de motif peuvent renvoyer les sous-chaînes capturées par les parenthèses dans le motif. Conformément aux règles de l'affectation de liste, cette affectation retournera vrai si quelque chose a été détecté et affecté, et faux dans le cas contraire. C'est pourquoi vous voyez parfois des choses comme :

 
Sélectionnez
if ( ($c,$v) = $chaine =~ m/(\w+)=(\w*)/ ) {
    print "CLE $c VALEUR $v\n";
}

Décomposons tout cela. Le =~ a la précédence sur =, il se produit donc en premier. Le =~ lie $chaine à la recherche de motif à droite. Celle-ci recherche ce qui ressemble à CLE=VALEUR dans votre chaîne. Il se trouve en contexte de liste, car du côté droit d'une affectation de liste. Si le motif est trouvé, il renvoie une liste à affecter à $c et $v. L'affectation de liste elle-même se trouve dans un contexte scalaire et renvoie donc 2, le nombre de valeurs à droite de l'affectation. Et il se trouve que 2 est vrai, puisque notre contexte scalaire est aussi un contexte booléen. Quand la recherche échoue, aucune valeur n'est affectée, ce qui renvoie 0, qui est faux.

Voir le chapitre 5, Recherche de motif, pour en savoir plus sur le fonctionnement des motifs.

3-7. Opérateurs multiplicatifs

Perl fournit des opérateurs proches de ceux de C : * (multiplication), / (division) et % (modulo). * et / fonctionnent exactement comme vous vous y attendez, en multipliant ou divisant leurs deux opérandes. La division se fait en virgule flottante, à moins que vous n'ayez spécifié le pragma integer.

L'opérateur % convertit ses opérandes en entiers avant de calculer le reste de la division entière. (Néanmoins cette division est faite en virgule flottante donc vos opérandes peuvent faire 15 chiffres sur la plupart des machines 32 bits.) Supposons que nos deux opérandes s'appellent $a et $b. Si $b est positif, alors le résultat de $a % $b est $a moins le plus grand multiple de $b inférieur à $a (ce qui veut dire que le résultat sera toujours dans l'intervalle 0 .. $b-1). Si $b est négatif, alors le résultat de $a % $b est $a moins le plus petit multiple de $b supérieur à $a (ce qui veut dire que le résultat sera toujours dans l'intervalle $b+1 .. 0).

Quand on est à portée de use integer, % vous donne directement accès à l'opérateur modulo tel qu'il est implémenté par votre compilateur C. Cet opérateur n'est pas bien défini pour les opérandes négatifs, mais s'exécutera plus rapidement.

Le x est l'opérateur de répétition. En réalité, il s'agit de deux opérateurs. En contexte scalaire, il retourne une chaîne concaténée composée de l'opérande de gauche répété le nombre de fois spécifié par l'opérande de droite. (Pour assurer la compatibilité avec les versions précédentes, il le fait également en contexte de liste si l'argument de gauche n'est pas entre parenthèses.)

 
Sélectionnez
print '-' x 80;                           # imprime une rangée de tirets
print "\t" x ($tab/8), ' ' x ($tab%8);    # tabule à la colonne $tab

En contexte de liste, si l'opérande de gauche est une liste entre parenthèses, x fonctionne comme un réplicateur de liste plutôt que comme un réplicateur de chaîne. Cela peut servir à initialiser à la même valeur tous les éléments d'un tableau de longueur indéterminée.

 
Sélectionnez
@ones = (1) x 80;       # une liste de 80 1
@ones = (5) x @ones;    # met tous les éléments à 5

De la même manière, vous vous pouvez également utiliser x pour initialiser des tranches de tableaux et de hachages.

 
Sélectionnez
@cles = qw(du Perl aux cochons);
@hash{@cles} = ("") x @cles;

Si cela vous laisse perplexe, remarquez que @cles est utilisé à la fois comme une liste à gauche de l'affectation et comme une valeur scalaire (qui renvoie la longueur du tableau) à droite. L'exemple précédent a le même effet sur %hash que :

 
Sélectionnez
$hash{du}      = "";
$hash{Perl}    = "";
$hash{aux}     = "";
$hash{cochons} = "";

3-8. Opérateurs additifs

Étonnamment, Perl dispose des opérateurs usuels + (addition) et - (soustraction). Ces deux opérateurs convertissent leurs arguments de chaîne en valeurs numériques si nécessaire, et renvoient un résultat numérique.

Perl fournit également l'opérateur . qui réalise la concaténation de chaînes. Par exemple :

 
Sélectionnez
$presque = "Fred" . "Pierrafeu";        # renvoie FredPierrafeu

Remarquez que Perl n'ajoute pas d'espace entre les chaînes concaténées. Si vous voulez l'espace ou si vous avez plus de deux chaînes à concaténer, vous pouvez utiliser l'opérateur join, décrit au chapitre 29, Fonctions. Cependant, la plupart du temps on concatène implicitement dans une chaîne entre doubles apostrophes :

 
Sélectionnez
$nomcomplet = "$prenom $nom";

3-9. Opérateurs de décalage

Les opérateurs de décalage de bits (<< et >>) renvoient la valeur de l'argument de gauche décalé à gauche (<<) ou à droite (>>) du nombre de bits spécifié par l'argument de droite. Les arguments doivent être des entiers. Par exemple :

 
Sélectionnez
1 << 4;     # renvoie 16
32 >> 4;    # renvoie 2

Faites attention cependant, car les résultats sur de grands nombres (ou sur des nombres négatifs) peuvent dépendre du nombre de bits utilisés par votre machine pour représenter les entiers.

3-10. Opérateurs unaires nommés et de test de fichier

Certaines des « fonctions » décrites au chapitre 29 sont en fait des opérateurs unaires. Le tableau 3-2 liste tous les opérateurs unaires nommés.

Tableau 3-2. Opérateurs unaires nommés

-X (tests de fichiers)

gethostbyname

localtime

return

alarm

getnetbyname

lock

rmdir

caller

getpgrp

log

scalar

chdir

getprotobyname

lstat

sin

chroot

glob

my

sleep

cos

gmtime

oct

sqrt

defined

goto

ord

srand

delete

hex

quotemeta

stat

do

int

rand

uc

eval

lc

readlink

ucfirst

exists

lcfirst

ref

umask

exit

length

require

undef

Les opérateurs unaires nommés ont une précédence supérieure à celle de certains opérateurs binaires. Par exemple :

 
Sélectionnez
sleep 4 | 3;

ne dort pas pendant 7 secondes ; il dort pendant 4 secondes puis prend la valeur de retour de sleep (typiquement zéro) et la combine par OU avec 3, comme si l'expression avait des parenthèses comme suit :

 
Sélectionnez
(sleep 4) | 3;

À comparer avec :

 
Sélectionnez
print 4 | 3;

qui prend effectivement la valeur de 4 combiné par OU avec 3 avant de l'afficher (7 dans ce cas), comme s'il était écrit :

 
Sélectionnez
print (4 | 3);

En effet, print est un opérateur de liste, et non un simple opérateur unaire. Une fois que vous saurez quels opérateurs sont des opérateurs de liste, vous n'aurez plus de problème pour distinguer les opérateurs de liste des opérateurs unaires. En cas de doute, vous pouvez toujours utiliser des parenthèses pour transformer un opérateur unaire en fonction. Souvenez-vous, si ça ressemble à une fonction, c'est une fonction.

Une autre caractéristique curieuse des opérateurs unaires nommés est que beaucoup d'entre eux prennent $_ comme argument par défaut, si vous ne leur en fournissez pas d'autre. Cependant, si vous omettez l'argument et que ce qui suit ressemble au début d'un argument, Perl va se tromper, car il attend un terme. Quand le caractère suivant du programme est l'un de ceux listés au tableau 3-3, le tokeniseur de Perl renvoie différents types de token selon qu'il attend un terme ou un opérateur.

Tableau 3-3. Caractères ambigus

Caractère

Opérateur

Terme

+

Addition

Plus unaire

-

Soustraction

Moins unaire

*

Multiplication

*typeglob

/

Division

/motif/

<

Inférieur à, décalage à gauche

<HANDLE>, <<END

.

Concaténation

.3333

?

?:

?motif?

%

Modulo

%assoc

&

&, &&

&sousprogramme

Une erreur typique est donc :

 
Sélectionnez
next if length < 80;

où pour l'analyseur le < ressemble au début du symbole <> (un terme) au lieu du « inférieur à » (un opérateur) auquel vous pensiez. Il n'existe pas vraiment de moyen d'arranger les choses tout en gardant Perl pathologiquement éclectique. Si vous êtes paresseux au point de ne pouvoir vous résoudre à taper les deux caractères $_, l'une de ces instructions devrait faire l'affaire :

 
Sélectionnez
next if length() < 80;
next if (length) < 80;
next if 80 > length;
next unless length >= 80;

Quand un terme est attendu, un signe moins suivi d'une lettre seule sera toujours interprété comme un test de fichier. Un opérateur de test de fichier est un opérateur unaire qui prend comme argument un nom ou un handle de fichier, et teste le fichier associé pour savoir si une certaine propriété est vraie à son sujet. Si l'argument est omis, il teste $_, sauf pour -t, qui teste STDIN. Sauf si c'est indiqué autrement dans la documentation, il retourne 1 pour vrai et "" pour faux, ou la valeur indéfinie si le fichier n'existe pas ou n'est pas accessible. Les opérateurs de test de fichier actuellement implémentés sont listés au tableau 3-4.

Tableau 3-4. Opérateurs de test de fichier

Opérateur

Signification

-r

Fichier lisible par l'UID/GID effectif.

-w

Fichier en écriture pour l'UID/GID effectif.

-x

Fichier exécutable par l'UID/GID effectif.

-o

Fichier possédé par l'UID/GID effectif.

-R

Fichier lisible par l'UID/GID réel.

-W

Fichier en écriture pour l'UID/GID réel.

-X

Fichier exécutable par l'UID/GID réel.

-O

Fichier possédé par l'UID/GID réel.

-e

Le fichier existe.

-z

Fichier de taille nulle.

-s

Fichier de taille non nulle (renvoie la taille)

-f

Fichier simple.

-d

Le fichier est un répertoire.

-l

Le fichier est un lien symbolique.

-p

Le fichier est un tube nommé (FIFO).

-S

Le fichier est une socket.

-b

Fichier spécial de type bloc

-c

Fichier spécial de type caractère.

-t

Handle de fichier ouvert sur un tty.

-u

Fichier avec bit setuid.

-g

Fichier avec bit setgid.

-k

Fichier avec sticky bit.

-T

Fichier texte.

-B

Fichier binaire (le contraire de -T).

-M

Âge du fichier (au démarrage) en jours depuis sa modification.

-A

Âge du fichier (au démarrage) en jours depuis le dernier accès.

-C

Âge du fichier (au démarrage) en jours depuis le changement d'inode.

Remarquez que -s/a/b/ ne fait pas une substitution négative. -exp($foo) fonctionne cependant comme prévu ; seules les lettres isolées qui suivent un signe moins sont interprétées comme des tests de fichier.

L'interprétation des opérateurs de test de permissions -r, -R, -w, -W, -x et -X est fondée uniquement sur le mode du fichier et les ID d'utilisateur et de groupe de l'utilisateur. Il peut y avoir d'autres raisons pour lesquelles vous ne pouvez effectivement pas lire, écrire ou exécuter le fichier, comme les listes de contrôle d'accès d'AFS (Andrew File System).(57) Notez également que pour le super utilisateur -r, -R, -w et -W renvoient toujours 1, et que -x et -X renvoient 1 si l'un des bits d'exécution est à 1 dans le mode. C'est pourquoi les scripts lancés par le super utilisateur peuvent nécessiter un stat ffin de connaître le véritable mode du fichier, ou bien remplacer temporairement l'UID par autre chose.

Les autres opérateurs de test de fichier se moquent de savoir qui vous êtes. N'importe qui peut utiliser le test pour les fichiers « simples » :

 
Sélectionnez
while (<>) {
    chomp;
    next unless -f $_;      # ignore les fichiers "spéciaux"
    ...
}

Les options -T et -B fonctionnent comme suit. Le premier bloc (plus ou moins) du fichier est examiné à la recherche de caractères inhabituels comme des codes de contrôle ou des octets donc le bit de poids fort est à 1 (et qui n'ont pas l'air d'être de l'UTF-8). Si on trouve plus d'un tiers d'octets inhabituels, c'est un fichier binaire ; sinon c'est un fichier texte. De même, tout fichier dont le premier bloc contient le caractère ASCII NUL (\0) est considéré comme binaire. Si -T ou -B est utilisé sur un handle de fichier, le tampon d'entrée standard (standard I/O ou « stdio ») en cours est examiné, au lieu du premier bloc du fichier. -T et -B renvoient vrai sur un fichier vide, ou sur un fichier à EOF (end-of-file : fin de fichier) quand on teste un handle. Comme Perl doit lire le fichier pour faire le test -T, vous devrez éviter de vous en servir sur des fichiers qui risquent de bloquer ou de vous causer des ennuis. C'est pourquoi la plupart du temps, vous devrez tester avec un -f d'abord, comme dans :

 
Sélectionnez
next unless -f $fichier && -T $fichier;

Si l'on donne à l'un des tests de fichier (ou à l'un des opérateurs stat ou lstat) le handle spécial constitué d'un souligné unique, c'est la structure stat du dernier test de fichier (ou de l'opérateur stat) qui est utilisée, économisant ainsi un appel système. (Cela ne marche pas avec -t, et il faudra vous souvenir que lstat et -l laissent dans la structure stat les valeurs pour le lien symbolique et non pour le fichier réel. De même, -l _ sera toujours faux après un stat normal.)

Voici quelques exemples :

 
Sélectionnez
print "Fait l'affaire.\n"    if -r $a || -w _ || -x _;

stat($fichier);
print "En lecture\n"         if -r _;
print "En écriture\n"        if -w _;
print "Exécutable\n"         if -x _;
print "Setuid\n"             if -u _;
print "Setgid\n"             if -g _;
print "Sticky\n"             if -k _;
print "Texte\n"              if -T _;
print "Binaire\n"            if -B _;

Les âges des fichiers donnés par -M, -A et -C sont en jours (avec partie fractionnaire) depuis le moment où le script a commencé à tourner. Cette date est stockée dans la variable spéciale $^T ($BASETIME). Donc, si le fichier a été modifié depuis que le script a démarré, vous obtiendrez une durée négative. Remarquez que comme la plupart des durées (86 399 sur 86 400 en moyenne) sont fractionnaires, il est généralement illusoire de tester l'égalité avec un entier sans passer par la fonction int. Exemples :

 
Sélectionnez
next unless -M $fichier > .5;       # fichier vieux de plus de 12 heures
&nouveaufichier if -M $fichier < 0; # fichier plus récent que le processus
&avertissemnt if int(-A) == 90;     # fichier ($_) accédé il y a 90 jours
                                    # aujourd'hui

Pour remettre à la date courante la date de démarrage du script, écrivez :

 
Sélectionnez
$^T = time;

3-11. Opérateurs relationnels

Perl possède deux classes d'opérateurs relationnels. L'une d'elles opère sur les valeurs numériques et l'autre sur les valeurs de chaîne, comme indiqué au tableau 3-5.

Tableau 3-5. Opérateurs relationnels

Numérique

Chaîne

Signification

>

gt

Supérieur à.

>=

ge

Supérieur ou égal à.

<

lt

Inférieur à.

<=

le

Inférieur ou égal à.

Ces opérateurs renvoient 1 pour vrai et "" pour faux. Notez que les opérateurs relationnels sont non-associatifs, ce qui signifie que $a < $b < $c provoquera une erreur de syntaxe.

En l'absence de déclaration de locales, les comparaisons de chaînes s'appuient sur l'ordre lexicographique ASCII/Unicode et, contrairement à certains autres langages informatiques, les espaces finaux comptent pour la comparaison. Avec une déclaration de locale, l'ordre lexicographique spécifié par la locale est utilisé. (Les mécanismes de tri fondés sur les locales peuvent plus ou moins bien interagir avec les mécanismes Unicode actuellement en cours de développement.)

3-12. Opérateurs d'égalité

Les opérateurs d'égalité listés au tableau 3-6 ressemblent beaucoup aux opérateurs relationnels.

Tableau 3-6. Opérateurs d'égalité

Numérique

Chaîne

Signification

==

eq

Égal à.

!=

ne

Différent de.

<=>

cmp

Comparaison, avec résultat signé.

Les opérateurs égal et différent renvoient 1 pour vrai et "" pour faux (exactement comme les opérateurs relationnels). Les opérateurs <=> et cmp renvoient -1 si l'opérande de gauche est inférieur à celui de droite, 0 s'il sont égaux et +1 si l'opérande de gauche est supérieur à celui de droite. Bien que ces opérateurs semblent très proches des opérateurs relationnels, ils ont un niveau de précédence plus faible, et la syntaxe de $a < $b <=> $c < $d est donc valide.

Pour des raisons évidentes pour quiconque a vu Star Wars, l'opérateur <=> est aussi connu sous le nom d'opérateur « vaisseau spatial » (spaceship operator).

3-13. Opérateurs sur les bits

Comme C, Perl dispose des opérateurs ET, OU et OU-exclusif sur les bits : &, | et ^. Vous avez bien sûr remarqué, après votre étude attentive du tableau au début de ce chapitre, que le ET sur les bits possède une précédence supérieure aux autres ; nous avons triché pour tous les inclure dans cette discussion.

Ces opérateurs fonctionnent différemment sur les valeurs numériques et sur les chaînes. (C'est l'un des rares cas où Perl fait une différence.) Si l'un des opérandes est un nombre (ou a été utilisé comme un nombre), les deux opérandes sont convertis en entiers, puis l'opération sur les bits est effectuée. Ces entiers font au moins 32 bits de long, mais peuvent faire 64 bits sur certaines machines. L'important est qu'il existe une limite arbitraire imposée par l'architecture de la machine.

Si les deux opérandes sont des chaînes (et n'ont pas été utilisés comme des nombres depuis leur dernière mise à jour), les opérateurs font les opérations sur les bits sur les bits correspondants des deux chaînes. Dans ce cas, il n'y a aucune limite arbitraire, puisque les chaînes ne sont pas limitées en taille. Si l'une des chaînes est plus longue que l'autre, on considère que la plus courte dispose d'un nombre suffisant de bits à 0 pour compléter la différence.

Par exemple, si vous faites un ET entre deux chaînes :

 
Sélectionnez
"123.45" & "234.56"

vous obtenez une autre chaîne :

 
Sélectionnez
"020.44"

Mais si vous faites un ET sur une chaîne et un nombre :

 
Sélectionnez
"123.45" & 234.56

La chaîne est d'abord convertie en nombre, ce qui donne :

 
Sélectionnez
123.45 & 234.56

Les nombres sont ensuite convertis en entiers :

 
Sélectionnez
123 & 234

ce qui donne 106. Remarquez que toutes les chaînes de bits sont vraies (à moins de donner la chaîne « 0 »). Cela signifie que si vous voulez vérifier si l'un des octets résultant est non nul, vous ne devrez pas écrire ceci :

 
Sélectionnez
if ( "fred" & "\1\2\3\4" ) { ... }

mais cela :

 
Sélectionnez
if ( ("fred" & "\1\2\3\4") =~ /[^\0]/ ) { ... }

3-14. Opérateurs logiques de type C (à court-circuit)

Comme C, Perl propose également les opérateurs && (ET logique) et || (OU logique). Ils sont évalués de gauche à droite (&& ayant une précédence légérement supérieure à celle de ||) pendant le test de vérité de l'instruction. Ces opérateurs sont appelés opérateurs court-circuit, car ils déterminent la vérité de l'instruction en évaluant le plus petit nombre d'opérandes possible. Par exemple, si l'opérande à gauche d'un opérateur && est faux, l'opérande de droite ne sera jamais évalué, car le résultat de l'opérateur sera faux quelle que soit la valeur de l'opérande de droite.

Exemple

Nom

Résultat

$a && $b

Et

$a si $a est faux, sinon $b.

$a || $b

Ou

$a si $a est vrai, sinon $b.

Non seulement de tels courts-circuits font gagner du temps, mais ils sont fréquemment utilisés pour contrôler le flux de l'évaluation. Par exemple, un idiome courant en Perl est :

 
Sélectionnez
open(FILE, "monfichier") || die "Impossible d'ouvrir monfichier: $!\n";

Dans ce cas, Perl évalue d'abord la fonction open. Si sa valeur est vraie (car monfichier a été ouvert avec succès), l'exécution de la fonction die n'est pas nécessaire et est donc omise. Vous pouvez littéralement lire « Ouvre mon fichier ou meurs ! ».

Les opérateurs && et || diffèrent de ceux de C en ceci qu'au lieu de retourner 0 ou 1, ils renvoient la dernière valeur évaluée. Dans le cas de ||, cela a le délicieux effet de vous permettre de choisir la première valeur vraie d'une série. Une manière raisonnablement portable de trouver le répertoire personnel d'un utilisateur pourrait donc être :

 
Sélectionnez
$home = $ENV{HOME}
     || $ENV{LOGDIR}
     || (getpwuid($<))[7]
     || die "Vous êtes SDF !\n";

D'un autre côté, comme l'argument de gauche toujours évalué en contexte scalaire, vous ne pouvez pas vous servir de || afin choisir entre deux agrégats pour une affectation :

 
Sélectionnez
@a = @b || @c;              # Ceci ne fait pas ce que vous voulez
@a = scalar(@b) || @c;      #, car voici ce que cela veut dire.
@a = @b ? @b : @c;          # En revanche cela marche bien.

Perl fournit également des opérateurs and et or de moindre précédence, que certains trouvent plus lisibles et qui ne vous forcent pas à utiliser des parenthèses sur les opérateurs de liste. Ils sont aussi à court-circuit. Voir le tableau 1-1 pour la liste complète.

3-15. Opérateur d'intervalle

L'opérateur d'intervalle .. comprend en fait deux opérateurs différents selon le contexte.

L'opérateur est bi-stable, comme un flip-flop électronique, et émule l'opérateur d'intervalle de ligne (virgule) de sed, awk et d'autres éditeurs. Chaque opérateur .. scalaire mémorise son propre état booléen. Il est faux tant que son opérande de gauche est faux. Une fois que l'opérande de gauche est vrai, l'opérateur reste vrai jusqu'à ce que l'opérande de droite soit vrai, après quoi l'opérateur d'intervalle redevient faux. L'opérateur ne devient pas faux jusqu'à sa prochaine évaluation. Il peut tester l'opérande de droite et devenir faux pendant la même évaluation que celle sur laquelle il est devenu vrai (comme l'opérateur de awk), mais renvoie vrai au moins une fois. Si vous ne voulez pas qu'il teste l'opérande de droite avant la prochaine évaluation (comme l'opérateur de sed) utilisez simplement trois points (...) au lieu de deux. Pour les deux opérateurs .. et ..., l'opérande de droite n'est pas évalué tant que l'opérateur se trouve dans l'état faux, et l'opérande de gauche n'est pas évalué tant que l'opérateur est dans l'état vrai.

La valeur renvoyée est soit la chaîne vide pour faux soit un numéro de séquence (commençant à 1) pour vrai. Le numéro de séquence est remis à zéro pour chaque opérateur d'intervalle rencontré. La chaîne « E0 » est accolée à la fin du numéro de séquence final dans un intervalle, ce qui n'affecte pas sa valeur numérique, mais vous donne quelque chose à chercher si vous voulez exclure l'élément final. Vous pouvez exclure le point de départ en attendant que le numéro de séquence soit plus grand que 1. Si l'un des opérandes de .. est un littéral numérique, cet opérande est implicitement comparé à la variable $., qui contient le numéro de ligne courant de votre fichier d'entrée. Exemples :

 
Sélectionnez
if (101 .. 200) { print; }    # imprime la deuxième centaine de lignes
next line if (1 .. /^$/);     # passe les entêtes d'un message
s/^/> / if (/^$/ .. eof());   # cite le corps d'un message

En contexte de liste, .. renvoie une liste de valeurs comptées à partir de la valeur de gauche jusqu'à la valeur de droite (par incréments de un). Cela sert à écrire des boucles for (1..10) et pour faire des tranches de tableaux :

 
Sélectionnez
for (101 .. 200) { print; }     # affiche 101102...199200
@foo = @foo[0 .. $#foo];        # un no-op coûteux
@foo = @foo[ -5 .. -1];         # la tranche des 5 derniers éléments

Si la valeur de gauche est supérieure à celle de droite, une liste vide est renvoyée. (Pour construire une liste en ordre inverse, voir l'opérateur reverse.)

Si ses opérandes sont des chaînes, l'opérateur d'intervalle emploie l'algorithme d'autoincrémentation magique vu précédemment.(58) Vous pouvez donc écrire :

 
Sélectionnez
@alphabet = ('A' .. 'Z');

pour obtenir toutes les lettres de l'alphabet (latin), ou :

 
Sélectionnez
$hexdigit = (0 .. 9, 'a' .. 'f')[$nombre & 15];

pour obtenir un chiffre hexadécimal, ou :

 
Sélectionnez
@z2 = ('01' .. '31'); print $z2[$jour];

pour obtenir des dates avec un zéro en tête. Vous pouvez également écrire :

 
Sélectionnez
@combis = ('aa' .. 'zz');

pour obtenir toutes les combinaisons de deux lettres minuscules. Attention cependant à quelque chose comme :

 
Sélectionnez
@grossescombis = ('aaaaaa' .. 'zzzzzz');

car cela va nécessiter beaucoup de mémoire. Pour être précis, il faudra de l'espace pour stocker 308 915 776 scalaires. Espérons que vous avez une grosse partition de swap. Vous devriez vous intéresser à une approche itérative.

3-16. Opérateur conditionnel

L'opérateur ?: est le seul opérateur ternaire, comme en C. On l'appelle souvent l'opérateur conditionnel, car il fonctionne comme un if-then-else, excepté qu'il peut être inclus sans problème dans d'autres expressions et fonctions, car c'est une expression et non une instruction. En tant qu'opérateur ternaire, ses deux éléments séparent trois expressions :

 
Sélectionnez
COND ? EXPR_SI_VRAI : EXPR_SI_FAUX

Si la condition COND est vrai, seule l'expression EXPR_SI_VRAI est évaluée, et la valeur de cette expression devient la valeur de toute l'expression. Sinon, seule l'expression EXPR_SI_FAUX est évaluée, et sa valeur devient celle de toute l'expression.

Le contexte scalaire ou de liste se propage vers le deuxième ou le troisième argument, selon celui qui est sélectionné. (Le premier argument est toujours en contexte scalaire, puisque c'est une condition.)

 
Sélectionnez
$a = $ok ? $b : $c; # donne un scalaire
@a = $ok ? @b : @c; # donne un tableau
$a = $ok ? @b : @c; # donne le nombre d'éléments d'un tableau

Vous verrez souvent l'opérateur conditionnel inclus dans des listes de valeurs à formater avec printf, car personne n'a envie de dupliquer une instruction complète juste pour basculer entre deux valeurs proches.

 
Sélectionnez
printf "J'ai %d chameau%s.\n",
             $n,       $n <= 1 ? "" : "x";

La précédence de ?: est opportunément plus grande que celle de la virgule, mais inférieure à celle de la plupart des opérateurs que vous utiliserez à l'intérieur (comme == dans cet exemple) ; vous n'aurez donc généralement pas à utiliser de parenthèses. Mais vous pouvez ajouter des parenthèses pour clarifier, si vous voulez. Pour les opérateurs conditionnels emboîtés à l'intérieur de la partie EXPR_SI_VRAI d'autres opérateurs conditionnels, nous vous suggérons de de faire des sauts de ligne et d'indenter comme s'il s'agissait d'instruction if ordinaires :

 
Sélectionnez
$bissextile =
    $annee % 4 == 0
        ? $annee % 100 == 0
            ? $annee % 400 == 0
                ? 1
                : 0
            : 1
        : 0;

Pour les conditions imbriquées dans des parties EXPR_SI_FAUX d'opérateurs conditionnels précédents, vous pouvez faire quelque chose d'équivalent :

 
Sélectionnez
$bissextile =
    $annee % 4
        ? 0
        : $annee % 100
            ? 1
            : $annee % 400
                ? 0
                : 1;

mais il est habituellement préférable d'aligner verticalement toutes les COND et les EXPR_SI_VRAI :

 
Sélectionnez
$bissextile =
    $annee % 4 ? 0 :
    $annee % 100 ? 1 :
    $annee % 400 ? 0 : 1;

Même des structures assez encombrées peuvent s'éclaircir en alignant les points d'interrogation et les deux-points :

 
Sélectionnez
printf "Oui, j'aime mon livre du %s!\n",
    $i18n eq "anglais" ? "camel" :
    $i18n eq "allemand" ? "Kamel" :
    $i18n eq "japonais" ? "\x{99F1}\x{99DD}" :
                          "chameau"

Vous pouvez affecter à l'opérateur conditionnel(59) si le deuxième et le troisième arguments sont tous les deux des lvalues légales (c'est-à-dire qu'on peut leur affecter une valeur), et que tous deux sont soit des scalaires, soit des listes (sinon Perl ne saura pas quel contexte fournir à la partie droite de l'affectation) :

 
Sélectionnez
($a_ou_b ? $a : $b) = $c;  # donne la valeur de $c soit à $a, soit à $b

Souvenez-vous que l'opérateur conditionnel lie encore plus fort que les multiples opérateurs d'affectation. C'est habituellement ce que vous voulez (voir l'exemple $bissextile plus haut, par exemple), mais vous ne pourrez pas obtenir l'effet inverse sans parenthèses. L'utilisation d'affectations dans un opérateur conditionnel vous attirera des ennuis, et vous risquez même de ne pas avoir d'erreur à l'analyse, car l'opérateur conditionnel peut être analysé comme une lvalue. Par exemple, vous pourriez écrire ceci :

 
Sélectionnez
$a% 2?$a+=10: $a += 2       # FAUX

Mais ce serait analysé comme cela :

 
Sélectionnez
(($a%2)?($a+=10) : $a) += 2

3-17. Opérateurs d'affectation

Perl reconnaît les opérateurs d'affectation de C, et fournit également les siens. Il y en a un certain nombre :

=

**=

+=

*=

&=

<<=

&&=

   

-=

/=

|=

>>=

||=

   

.=

%=

^=

   
     

x=

     

Chaque opérateur nécessite une lvalue cible (typiquement une variable ou un élément de tableau) à sa gauche et une expression à sa droite. Pour l'opérateur d'affectation simple :

 
Sélectionnez
CIBLE = EXPR

La valeur de EXPR est stockée dans la variable ou à l'endroit désigné par CIBLE. Pour les autres opérateurs, Perl évalue l'expression :

 
Sélectionnez
CIBLE OP= EXPR

comme s'il était écrit :

 
Sélectionnez
CIBLE = CIBLE OP EXPR

C'est un bon moyen mnémotechnique, mais il est trompeur, et de deux manières. D'abord les opérateurs d'affectation sont analysés au niveau de précédence des affectations ordinaires, quelle que soit la précédence que l'opérateur OP aurait eue par lui-même. Ensuite, CIBLE n'est évalué qu'une seule fois. Habituellement cela n'a aucune importance, sauf en cas d'effets secondaires, comme pour une auto-incrémentation.

 
Sélectionnez
$var[$a++] += $valeur;               # $a is incrementé une fois
$var[$a++] = $var[$a++] + $valeur;   # $a is incrementé deux fois

À la différence de C, l'opérateur d'affectation produit une lvalue valide. Modifier une affectation revient à faire l'affectation puis à modifier la variable à laquelle on vient d'affecter une valeur. Cela peut servir à modifier une copie de quelque chose, comme ceci :

 
Sélectionnez
($tmp = $global) += $constante;

qui est équivalent à :

 
Sélectionnez
$tmp = $global + $constante;

De même :

 
Sélectionnez
($a +=2)*= 3;

est équivalent à :

 
Sélectionnez
$a += 2;
$a *= 3;

Ce n'est pas tellement utile, mais voici un idiome fréquent :

 
Sélectionnez
($nouveau = $ancien) =~ s/toto/titi/g;

Dans tous les cas, la valeur de l'affectation est la nouvelle valeur de la variable. Comme les opérateurs d'affectations sont associatifs de droite à gauche, cela peut servir à affecter la même valeur à plusieurs variables, comme dans :

 
Sélectionnez
$a=$b =$c = 0;

qui affecte 0 à $c et le résultat de cette opération (toujours 0) à $b, puis le résultat de (toujours 0) à $a.

Les affectations de listes ne se font qu'avec l'opérateur d'affectation simple, =. Dans un contexte de liste, une affectation de liste retourne la liste des nouvelles valeurs, tout comme l'affectation scalaire. En contexte scalaire, une affectation de liste renvoie le nombre de valeurs qui étaient disponibles à droite de l'affectation, comme indiqué au chapitre 2, Composants de Perl. Cela sert pour tester les fonctions qui renvoient une liste vide quand elles échouent (ou finissent par échouer), comme dans :

 
Sélectionnez
while (($cle, $valeur) = each %gloss) { ... }
next unless ($dev, $ino, $mode) = stat $fichier;

3-18. Opérateurs virgule

Le « , » binaire est l'opérateur virgule. En contexte scalaire, il évalue son argument de gauche en contexte vide, jette le résultat, puis évalue son argument de droite et retourne cette valeur. Exactement comme l'opérateur virgule de C. Par exemple :

 
Sélectionnez
$a = (1, 3);

affecte 3 à $a. Attention à ne pas confondre son utilisation en contexte scalaire avec son utilisation en contexte de liste. Dans un contexte de liste, une virgule est juste le séparateur des arguments de la liste et il insère ses deux arguments dans la LISTE. Il ne jette aucune valeur.

Par exemple, si vous modifiez l'exemple précédent en :

 
Sélectionnez
@a = (1, 3);

vous construisez une liste de deux éléments, tandis que :

 
Sélectionnez
atan2(1, 3);

appelle la fonction atan2 avec deux arguments.

La plupart du temps, le digramme => est juste un synonyme pour l'opérateur virgule. Il sert à documenter les arguments appariés. Il force également l'interprétation comme chaîne de tout identificateur placé à sa gauche.

3-19. Opérateurs de liste (vers la droite)

La partie droite d'un opérateur de liste commande tous les arguments de l'opérateur de liste, qui sont séparés par des virgules ; donc la précédence d'un opérateur de liste est plus faible que celle de la virgule, si vous regardez vers la droite. Une fois qu'un opérateur de liste commence à avaler des arguments séparés par des virgules, les seules choses qui l'arrêtent sont les tokens qui terminent l'expression tout entière (comme les points-virgules ou les modificateurs d'instructions) ou les tokens qui terminent la sous-expression en cours (comme les parenthèses ou les crochets fermants) ou les opérateurs logiques de faible précédence dont nous aller parler tout de suite.

3-20. And, or, not et xor logiques

Perl fournit les opérateurs and, or, et not comme alternatives de moindre précédence à &&, ||, et !. Le comportement de ces opérateurs est identique — en particulier, and et or court-circuitent comme leurs alter ego, ce qui leur donne leur utilité non seulement pour les expressions logiques, mais aussi pour le contrôle du flux d'évaluation.

Comme la précédence de ces opérateurs est beaucoup plus basse que celle des opérateurs empruntés à C, vous pouvez les utiliser en toute sécurité après un opérateur de liste, sans qu'il soit besoin de parenthèses.

 
Sélectionnez
unlink "alpha", "beta", "gamma"
        or enrage(), next LIGNE;

Avec les opérateurs issus de C, vous auriez dû l'écrire comme ceci :

 
Sélectionnez
unlink("alpha", "beta", "gamma")
        || (enrage(), next LIGNE);

Mais vous ne pouvez pas simplement remplacer toutes les occurrences de || par or. Supposons que vous changiez ceci :

 
Sélectionnez
$xyz=$x|| $y|| $z;

en cela :

 
Sélectionnez
$xyz=$xor$yor $z; # FAUX

Cela ne ferait pas du tout la même chose ! La précédence de l'affectation est supérieure à celle de or, mais plus basse que celle de ||, donc on affecterait toujours $x à $xyz pour ensuite seulement faire les or. Pour obtenir le même résultat qu'avec ||, vous devrez écrire :

 
Sélectionnez
$xyz= ($xor $y or $z );

La morale de cette histoire, c'est qu'il faut toujours apprendre les règles de précédence (ou utiliser les parenthèses) quels que soient les opérateurs logiques que vous utilisez.

Il existe également un xor logique qui n'a pas d'équivalent exact en C ou en Perl, puisque le seul autre opérateur OU-exclusif (^) travaille sur les bits. L'opérateur xor ne peut pas court-circuiter, car les deux côtés doivent être évalués. Le meilleur équivalent de $a xor $b est peut-être !$a != !$b. On pourrait également écrire !$a ^ !$b ou même $a ? !$b : !!$b, bien sûr. L'essentiel est que $a et $b doivent tous les deux être évalués comme vrais ou faux dans un contexte booléen, et l'opérateur sur les bits existants n'en fournit pas sans qu'on l'y aide.

3-21. Opérateurs C manquant en Perl

Voici ce que C a et que Perl n'a pas :

& unaire

  • L'opérateur adresse-de. L'opérateur \ de Perl (qui fournit une référence) occupe la même niche écologique :

     
    Sélectionnez
    $ref_var = \$var;
  • Mais les références de Perl sont plus sûres que les pointeurs de C.

* unaire

  • L'opérateur de déréférencement d'adresse. Comme Perl ne connaît pas les adresses, il n'a pas besoin de les déréférencer. En revanche, Perl a des références et ce sont donc les caractères de préfixe variable qui servent d'opérateurs de déréférence, tout en indiquant le type : $, @, % et &. Étonnamment, il existe bien un opérateur * de déréférencement, mais comme * est le drôle de caractère indiquant un typeglob, vous ne l'utiliserez pas ainsi.

(TYPE)

  • L'opérateur de transtypage. De toute façon, personne n'aime se faire transtyper.

4. Instructions et déclarations

Un programme Perl consiste en une séquence de déclarations et d'instructions. Une déclaration peut-être placée partout où une instruction peut l'être, mais son effet premier se produit à la compilation. Certaines déclarations jouent un double rôle en tant qu'instructions, mais la plupart sont totalement transparentes à l'exécution. Après la compilation, la séquence d'instructions principale est exécutée une seule fois.

Contrairement à beaucoup de langages de programmation, Perl n'impose pas de déclarer explicitement les variables ; elles se mettent à exister à leur première utilisation, que vous les ayez déclarées ou non. Si vous essayez d'utiliser la valeur d'une variable à laquelle aucune valeur n'a jamais été affectée, elle est traitée silencieusement comme si elle contenait 0 si vous vouliez un nombre, comme "" si vous vouliez une chaîne ou simplement comme une valeur fausse si vous vouliez une valeur logique. Si vous préférez être averti de l'utilisation de valeurs non définies comme si elles étaient de véritables chaînes ou nombres, ou même traiter cette utilisation comme une erreur, la déclaration use warnings s'en chargera ; voir la section Pragmas à la fin de ce chapitre.

Cependant, si vous préférez, vous pouvez éventuellement déclarer vos variables, en utilisant soit my ou our avant le nom de variable. Vous pouvez même faire qu'utiliser une variable non déclarée soit une erreur. C'est bien de vouloir de la discipline, encore faut-il la demander. Normalement, Perl ne s'intéresse pas à vos habitudes de programmation ; mais avec la déclaration use strict, l'utilisation de variables non déclarées est détectée à la compilation. De nouveau, voir la section Pragmas.

4-1. Instructions simples

Une instruction simple est une expression évaluée pour ses effets secondaires. Toute instruction simple doit se terminer par un point-virgule, sauf si c'est la dernière instruction d'un bloc. Dans ce cas, le point-virgule est optionnel — Perl sait que vous en avez fini avec cette instruction, puisque vous avez fini le bloc. Mais mettez quand même le point-virgule s'il s'agit d'un bloc multiligne, car vous pourriez bien ajouter une autre ligne par la suite.

Bien que des opérateurs comme eval {}, do {} et sub {} ressemblent à des instructions composées, en fait ce n'en sont pas. Certes, ils permettent d'avoir plusieurs instructions à l'intérieur, mais cela ne compte pas. Vus de l'extérieur, ces opérateurs sont juste des termes dans une expression ; c'est pourquoi ils nécessitent un point-virgule explicite quand ils sont utilisés comme dernier élément d'une instruction.

Toute instruction simple peut être optionnellement suivie d'un modificateur unique, juste avant le point-virgule final (ou la fin de bloc). Les modificateurs possibles sont :

 
Sélectionnez
if EXPR
unless EXPR
while EXPR
until EXPR
foreach LISTE

Les modificateurs if et unless fonctionnent comme peuvent s'y attendre les anglophones :

 
Sélectionnez
$trash->take('out') if $you_love_me;    # sors la poubelle si tu m'aimes
shutup() unless $you_want_me_to_leave;  # tais-toi, sauf si tu veux que
                                        # je m'en aille

Les modificateurs while et until sont évalués de façon répétée. Comme notre lectorat anglophone pouvait s'y attendre, un modificateur while exécute l'expression tant que sa propre expression reste vraie, tandis qu'un modificateur until continue de s'exécuter tant qu'elle reste fausse :

 
Sélectionnez
$expression++ while -e "$file$expression";
kiss('me') until $I_die;                # embrasse-moi jusqu'à la mort

Le modificateur foreach (aussi orthographié for) est évalué une fois par élément de sa LISTE, $_ étant un alias de l'élément courant :

 
Sélectionnez
s/java/perl/ for @curriculum;
print "champ: $_\n" foreach split /:/, $ligne;

Les modificateurs while et until ont la sémantique usuelle des boucles while (la condition est évaluée en premier), sauf quand ils s'appliquent à un doBLOC (ou à l'instruction maintenant dépréciée doSUBROUTINE), auquel cas le bloc s'exécute une fois avant que la condition soit évaluée. Cela vous permet d'écrire des boucles comme :

 
Sélectionnez
do {
    $ligne = <STDIN>;
    ...
} until $ligne eq ".\n";

Voyez aussi les trois différentes entrées de do au chapitre 29, Fonctions. Remarquez également que les opérateurs de contrôle de boucle décrits plus loin ne fonctionnent pas dans cette construction, car les modificateurs de boucles ne prennent pas d'étiquette. Vous pouvez toujours placer un bloc supplémentaire autour pour terminer plus tôt, ou à l'intérieur pour itérer avant la fin de la boucle, comme cela est décrit dans la section Blocs simples. Ou bien vous pourriez écrire une vraie boucle avec plusieurs commandes de contrôle de boucle à l'intérieur. À propos de vraies boucles, nous allons maintenant parler des instructions composées.

4-2. Instructions composées

On appelle bloc une séquence d'instructions définie dans une portée(60). Parfois la portée s'étend sur un fichier tout entier, par exemple un fichier appelé avec require ou le fichier contenant votre programme principal. D'autres fois la portée a l'étendue d'une chaîne évaluée avec eval. Mais en général, un bloc est entouré d'accolades ({}). Quand nous parlons de portée, cela signifie n'importe laquelle de ces trois possibilités. Quand nous voudrons dire un bloc entouré d'accolades, nous emploierons le terme BLOC.

Les instructions composées sont construites à partir d'expressions et de BLOC. Les expressions sont construites à partir de termes et d'opérateurs. Dans nos descriptions syntaxiques, nous utiliserons le mot EXPR pour indiquer un emplacement où vous pouvez utiliser n'importe quelle expression scalaire. Pour indiquer une expression évaluée en contexte de liste, nous dirons LISTE.

Les constructions suivantes peuvent être utilisées pour contrôler l'exécution de BLOC de façon conditionnelle ou répétée. (L'étiquette LABEL est optionnelle.)

 
Sélectionnez
if (EXPR) BLOC
if (EXPR) BLOC else BLOC
if (EXPR) BLOC elsif (EXPR) BLOC ...
if (EXPR) BLOC elsif (EXPR) BLOC ... else BLOC

unless (EXPR) BLOC
unless (EXPR) BLOC else BLOC
unless (EXPR) BLOC elsif (EXPR) BLOC ...
unless (EXPR) BLOC elsif (EXPR) BLOC ... else BLOC

LABEL while (EXPR) BLOC
LABEL while (EXPR) BLOC continue BLOC
LABEL until (EXPR) BLOC
LABEL until (EXPR) BLOC continue BLOC

LABEL for (EXPR; EXPR; EXPR) BLOC

LABEL foreach (LISTE) BLOC
LABEL foreach VAR (LISTE) BLOC
LABEL foreach VAR (LISTE) BLOC continue BLOC

LABEL BLOC
LABEL BLOC continue BLOC

Remarquez qu'à l'inverse de C et de Java, elles sont définies en termes de BLOC, et non d'instructions. Cela signifie que les accolades sont obligatoires ; les instructions isolées ne sont pas autorisées. Si vous voulez écrire des conditions sans accolades, il y a plusieurs manières de le faire. Les lignes suivantes font toutes la même chose :

 
Sélectionnez
unless (open(TOTO, $toto))      { die "Impossible d'ouvrir $toto: $!" }
if (!open(TOTO, $toto))         { die "Impossible d'ouvrir $toto: $!" }

die "Impossible d'ouvrir $toto: $!"     unless open(TOTO, $toto);
die "Impossible d'ouvrir $toto: $!"     if !open(TOTO, $toto);

open(TOTO, $toto)               || die "Impossible d'ouvrir $toto: $!";
open TOTO, $toto                or die "Impossible d'ouvrir $toto: $!";

Nous avons tendance à préférer les deux dernières dans la plupart des cas. Elles sont plus lisibles que les autres, en particulier la version « or die ». Avec || vous devez vous habituer à utiliser les parenthèses religieusement, tandis qu'avec la version or, ce n'est pas grave de les oublier.

Mais la principale raison pour laquelle nous préférons les dernières versions est qu'elles mettent la partie la plus importante de l'instruction au début de la ligne, là où vous la verrez le mieux. La gestion d'erreur est repoussée vers la droite, où vous n'avez pas besoin d'y faire attention, sauf si vous le voulez2. Et si vous tabulez tous vos tests « or die » à la même position à droite de chaque ligne, c'est encore plus facile à lire :

 
Sélectionnez
chdir $dir                  or die "chdir $dir: $!";
open TOTO, $fichier         or die "open $fichier: $!";
@lines = <TOTO>             or die "$fichier est vide ?";
close TOTO                  or die "close $fichier: $!";

4-3. Instructions if et unless

L'instruction if est simple. Comme les BLOCs sont délimités par des accolades, il n'y jamais d'ambiguïté pour savoir à quel if en particulier un else ou un elsif est lié. Dans une séquence donnée de BLOCs if/elsif/else, seul le premier dont la condition est vraie est exécuté. Si aucune des conditions n'est vraie, alors le BLOC else, s'il existe, est exécuté. C'est en général une bonne idée de mettre un else à la fin d'une chaîne de elsif, afin de se prémunir contre un cas oublié.

Si vous utilisez unless à la place de if, le sens du test est inversé. C'est-à-dire que :

 
Sélectionnez
unless ($x == 1) ...

est équivalent à :

 
Sélectionnez
if ($x != 1) ...

ou au plus laid :

 
Sélectionnez
if (!($x == 1)) ...

La portée d'une variable déclarée dans la condition de contrôle s'étend de sa déclaration jusqu'à la fin de l'instruction conditionnelle, y compris tous les elsif et l'éventuelle clause else finale, mais pas plus loin :

 
Sélectionnez
if ((my $couleur = <STDIN>) =~ /rouge/i) {
    $valeur = 0xff0000;
}
elsif ($couleur =~ /vert/i) {
    $valeur = 0x00ff00;
}
elsif ($couleur =~ /bleu/i) {
    $valeur = 0x0000ff;
}
else {
    warn "`$couleur' : composante RGB inconnue, le noir est sélectionné\n";
    $valeur = 0x000000;
}

Après le else, la variable $couleur est hors de portée. Si vous voulez que sa portée s'étende plus loin, déclarez la variable plus tôt.

4-4. Instructions de boucle

Dans leur syntaxe formelle, toutes les instructions de boucle comportent un LABEL (ou étiquette) facultatif. (Vous pouvez mettre une telle étiquette sur n'importe quelle instruction, mais cela a une signification spéciale pour les boucles.) S'il est présent, le label consiste en un identificateur suivi de deux-points. Il est courant d'écrire l'étiquette en majuscules pour éviter tout conflit avec des mots réservés et les faire mieux ressortir. Bien que Perl ne se pose pas de problème si vous utilisez un label qui a déjà une signification comme if ou open, vos lecteurs risquent de se tromper.

4-4-a. Instructions while et until

L'instruction while exécute le bloc tant que EXPR est vraie. Si le mot while est remplacé par until, le sens du test est inversé ; c'est-à-dire qu'il exécute le bloc tant que EXPR est fausse. La condition est cependant toujours testée avant la première itération.

Les instructions while ou until comportent un bloc supplémentaire facultatif : le bloc continue. Ce bloc est exécuté à chaque fois que l'on achève l'itération, soit en sortant à la fin du premier bloc, soit par un next explicite (next est un opérateur de contrôle de boucle qui passe à l'itération suivante). En pratique, le bloc continue n'est pas très utilisé, mais il est présent afin de pouvoir définir rigoureusement la boucle for dans la section qui suit.

Contrairement à la boucle for que nous allons voir dans un moment, une boucle while ne localise jamais implicitement de variables dans sa condition de test. Cela peut avoir d'« intéressantes » conséquences quand des boucles while utilisent des variables globales comme variables de boucle. En particulier, consultez la section Opérateur de lecture de ligne (Angle) au chapitre 2 pour voir comment une affectation implicite à la variable globale $_ peut se produire dans certaines boucles while, ainsi qu'un exemple de gestion de ce problème en localisant explicitement $_. Il est cependant préférable de déclarer les autres variables de boucle avec my, comme dans l'exemple suivant.

Une variable déclarée dans la condition de test d'une instruction while ou d'un until est visible seulement dans le ou les blocs pilotés par ce test. Sa portée ne s'étend pas au-delà. Par exemple :

 
Sélectionnez
while (my $ligne = <STDIN>) {
    $ligne = lc $ligne;
}
continue {
    print $ligne;   # toujours visible
}
# $ligne maintenant hors de portée

Ici, la portée $ligne s'étend de sa déclaration dans l'expression de contrôle à l'ensemble de la boucle, y compris le bloc continue, mais pas au-delà. Si vous voulez que sa portée s'étende plus loin, déclarez la variable avant la boucle.

4-4-b. Boucles for

La boucle for en trois parties comporte trois expressions séparées par des points-virgules entre ses parenthèses. Ces expressions sont respectivement l'initialisation, la condition et la réinitialisation de la boucle. Ces trois expressions sont optionnelles (mais pas les points-virgules) ; si la condition est omise, elle est toujours vraie. La boucle for peut être définie dans les termes de la boucle while correspondante. Ce qui suit :

 
Sélectionnez
LABEL:
  for (my $i = 1; $i <= 10; $i++) {
      ...
  }

est donc identique à :

 
Sélectionnez
{
    my $i = 1;
  LABEL:
    while ($i <= 10) {
        ...
    }
    continue {
        $i++;
    }
}

sinon qu'il n'y a pas vraiment de bloc extérieur. (Nous l'avons juste mis là pour montrer les limites du my.)

Si vous voulez itérer deux variables simultanément, il vous suffit de séparer les expressions parallèles par des virgules :

 
Sélectionnez
for ($i = 0, $bit = 0; $i < 32; $i++, $bit <<= 1) {
    print "Le bit $i is est à 1\n" if $mask & $bit;
}
# les valeurs de $i et $bit persistent après la boucle

Ou bien déclarer ces variables comme visibles seulement à l'intérieur de la boucle for:

 
Sélectionnez
for (my ($i, $bit) = (0, 1); $i < 32; $i++, $bit <<= 1) {
    print "Le bit $i is est à 1\n" if $mask & $bit;
}
# les $i et $bit de la boucle sont maintenant hors de portée

En plus des boucles habituelles sur les indices de tableaux, for a d'autres applications intéressantes. Il n'a même pas besoin d'une variable de boucle explicite. Voici un exemple permettant d'éviter le problème que vous rencontrez en testant explicitement la fin de fichier sur un descripteur de fichier interactif, provoquant ainsi le blocage du programme.

 
Sélectionnez
$sur_un_tty = -t STDIN && -t STDOUT;
sub prompt { print "yes? " if $sur_un_tty }
for ( prompt(); <STDIN>; prompt() ) {
    # fait quelque chose
}

Une autre application traditionnelle pour le for en trois parties vient du fait que les trois expressions sont optionnelles et que la condition par défaut est vraie. Si vous omettez les trois expressions, vous obtenez une boucle infinie :

 
Sélectionnez
for (;;) {
    ...
}

Ce qui est exactement équivalent à :

 
Sélectionnez
while (1) {
    ...
}

Si la notion de boucle infinie vous inquiète, nous devrions souligner que vous pouvez toujours sortir de la boucle quand vous voulez avec un opérateur de contrôle explicite de boucle comme last. Bien sûr, si vous écrivez le code de contrôle d'un missile de croisière, vous n'aurez jamais vraiment besoin de sortir de la boucle. Elle se terminera automatiquement au moment opportun.(61)

4-4-c. Boucles foreach

La boucle foreach parcourt une liste de valeurs en affectant tour à tour chaque élément de la liste à la variable de contrôle (VAR) :

 
Sélectionnez
foreach VAR (LISTE) {
    ...
}

Le mot-clef foreach est juste un synonyme du mot-clef for, vous pouvez donc utiliser au choix for et foreach, selon lequel vous trouvez le plus lisible dans une situation donnée. Si VAR est omis, la variable globale $_ est utilisée. (Ne vous inquiétez pas — Perl distingue facilement for (@ARGV) de for ($i=0; $i<$#ARGV; $i++), car ce dernier contient des points-virgules.) Voici quelques exemples :

 
Sélectionnez
$somme = 0; foreach $valeur (@tableau) { $somme += $valeur }

for $decompte (10,9,8,7,6,5,4,3,2,1,'BOUM') {   # compte à rebours
    print "$decompte\n"; sleep(1);
}

for (reverse 'BOUM', 1 .. 10) {                 # pareil
    print "$_\n"; sleep(1);
}

for $champ (split /:/, $data) {                 # toute expression de LISTE
    print "Le champ contient : '$champ'\n";
}

foreach $cle (sort keys %hash) {
    print "$cle => $hash{$cle}\n";
}

Le dernier représente la manière classique d'afficher les valeurs d'un hachage dans l'ordre des clefs. Consultez les entrées keys et sort au chapitre 29 pour des exemples plus élaborés.

Il n'existe aucune manière de savoir où vous en êtes dans une liste avec foreach. Vous pouvez comparer des éléments adjacents en vous souvenant du précédent dans une variable, mais il faudra parfois vous contenter d'écrire une boucle for en trois parties avec des indices. Après tout, cet autre for est justement là pour ça.

Si LISTE est entièrement constituée d'éléments auxquels on peut affecter une valeur (c'est-à-dire de variables, pas d'une énumération de constantes), vous pouvez modifier chacune de ces variables en modifiant VAR à l'intérieur de la boucle. Cela provient du fait que la variable d'indice de la boucle foreach est un alias implicite de chaque élément de la liste sur laquelle vous bouclez. Vous pouvez non seulement modifier un tableau d'un coup, mais également plusieurs tableaux ou hachages dans une seule liste :

 
Sélectionnez
foreach $paye (@salaires) {             # une augmentation de 8%
    $paye *= 1.08;
}

for (@noel, @paques) {                  # change le menu
    s/pâté/foie gras/;
}
s/pâté/foie gras/ for @noel, @paques;   # idem

for ($scalaire, @tableau, values %hash) {
    s/^\s+//;                           # retire les espaces initiaux
    s/\s+$//;                           # retire les espaces finaux
}

La variable de boucle est valide uniquement dans la portée dynamique ou lexicale de la boucle et sera implicitement lexicale si la variable a été précédemment déclarée avec my. Cela la rend invisible à toute fonction définie en dehors de la portée lexicale de la variable, même si elle est appelée depuis cette boucle. Cependant si aucune déclaration lexicale n'est à portée, la variable de boucle sera une variable globale localisée (à portée dynamique) ; cela permet aux fonctions appelées depuis la boucle d'accéder à cette variable. Dans tous les cas, la valeur qu'avait la variable localisée avant la boucle sera restaurée à la sortie de la boucle.

Si vous préférez, vous pouvez déclarer explicitement quel type de variable (lexicale ou globale) utiliser. Ceux qui maintiennent votre code sauront plus facilement ce qui se passe ; sinon, ils devront remonter la chaîne des portées successives à la recherche d'une déclaration pour deviner de quelle variable il s'agit :

 
Sélectionnez
for my  $i    (1 .. 10) { ... }       # $i toujours lexicale
for our $Tick (1 .. 10) { ... }       # $Tick toujours globale

Quand une déclaration accompagne la variable de boucle, l'écriture courte for est toujours préférable à foreach, car cela se lit mieux en anglais.(62)

Voici comment un programmeur C ou Java pourrait d'abord penser à écrire un algorithme donné en Perl :

 
Sélectionnez
for ($i = 0; $i < @tab1; $i++) {
    for ($j = 0; $j < @tab2; $j++) {
        if ($tab1[$i] > $tab2[$j]) {
            last; # Impossible d'aller à la boucle externe. :-(
        }
        $tab1[$i] += $tab2[$j];
    }
    # voici où ce last m'emmène
}

Mais voici comment un programmeur Perl expérimenté pourrait l'écrire :

 
Sélectionnez
WID: foreach $ceci (@tab1) {
    JET: foreach $cela (@tab2) {
        next WID if $ceci > $cela;
        $ceci += $cela;
    }
}

Vous voyez combien c'était simple en Perl idiomatique ? C'est plus propre, plus sûr et plus rapide. C'est plus propre, car il y a moins de bruit. C'est plus sûr, car si du code est ajouté par la suite entre les boucles interne et externe, ce code ne sera pas accidentellement exécuté, car next (expliqué plus loin) reboucle sur la boucle externe.

Mais vous codez comme vous préférez. TMTOWTDI.

Comme l'instruction while, l'instruction foreach peut aussi avoir un bloc continue. Cela vous permet d'exécuter un bout de code à la fin de chaque itération de boucle, que vous en soyez arrivé là par le cours normal des événements ou par un next.

4-4-d. Contrôle de boucle

Nous avons déjà mentionné que vous pouviez mettre un LABEL sur une boucle pour lui donner un nom. L'étiquette identifie la boucle pour les opérateurs de contrôle de boucle next, last et redo. Le LABEL désigne la boucle tout entière, pas seulement le début de celle-ci. Une commande de contrôle de boucle ne « va » pas au LABEL lui-même. Pour l'ordinateur l'étiquette aurait aussi bien pu être placée à la fin de la boucle. Mais il semble que les gens préfèrent les étiquettes au début.

Les boucles sont typiquement nommées d'après les éléments qu'elles manipulent à chaque itération. Cela se combine bien avec les opérateurs de contrôle de boucle, qui sont conçus pour se lire comme de l'anglais quand ils sont utilisés avec une étiquette appropriée et un modificateur d'instruction. La boucle typique traite des lignes, donc l'étiquette de boucle typique est LINE: ou LIGNE: et l'opérateur de contrôle de ligne typique ressemble à ceci :

 
Sélectionnez
next LIGNE if /^#/; # supprime les commentaires

La syntaxe des opérateurs de contrôle de boucle est :

 
Sélectionnez
last LABEL
next LABEL
redo LABEL

Le LABEL est optionnel ; s'il est omis, l'opérateur se réfère à la boucle englobante la plus interne. Mais si vous voulez sauter plus d'un niveau, vous devez utiliser un LABEL pour désigner la boucle sur laquelle agir. Ce LABEL n'a pas besoin d'être dans la même portée lexicale que l'opérateur de contrôle, mais c'est probablement préférable. En fait, le LABEL peut être n'importe où dans la portée dynamique. Si cela vous force à sortir d'un eval ou d'un sous-programme, Perl émet un avertissement (sur demande).

Tout comme vous pouvez avoir autant de return que vous voulez dans une fonction, vous pouvez avoir autant d'opérateurs de contrôle de boucle que vous voulez dans une boucle. Cela n'a pas à être considéré comme mauvais ou pas cool. Aux débuts de la programmation structurée, certaines personnes insistaient sur le fait que les boucles et les sous-programmes ne devaient avoir qu'une entrée et qu'une sortie. La notion d'entrée unique est toujours une bonne idée, mais la notion de sortie unique a conduit à l'écriture de beaucoup de code artificiel. La programmation consiste principalement à parcourir des arbres de décision. Un arbre de décision commence naturellement par une racine unique, mais se termine par de nombreuses feuilles. Écrivez votre code avec le nombre de sorties de boucle (et de retours de fonction) qui est naturel pour le problème que vous essayez de résoudre. Si vous avez déclaré vos variables dans des portées raisonnables, tout sera automatiquement nettoyé le moment venu, quelle que soit la manière dont vous quittez le bloc.

L'opérateur last sort immédiatement de la boucle en question. Le bloc continue, s'il existe, n'est pas exécuté. L'exemple suivant s'éjecte de la boucle à la première ligne blanche :

 
Sélectionnez
LIGNE: while (<STDIN>) {
    last LIGNE if /^$/;       # sort quand l'entête de mail est fini
    ...
}

L'opérateur next passe le reste de l'itération courante de la boucle et commence la suivante. S'il existe une clause continue sur la boucle, elle est exécutée juste avant que la condition soit réévaluée, exactement comme la troisième partie d'une boucle for. Elle peut donc servir à incrémenter une variable de boucle, même si une itération particulière de la boucle a été interrompue par un next :

 
Sélectionnez
LIGNE: while (<STDIN>) {
    next LIGNE if /^#/;     # ignore les commentaires
    next LIGNE if /^$/;     # ignore les lignes blanches
    ...
} continue {
    $compte++;
}

L'opérateur redo redémarre le bloc de boucle sans réévaluer la condition. Le bloc continue, s'il existe, n'est pas exécuté. Cet opérateur s'utilise souvent pour des programmes qui veulent se cacher à eux-mêmes ce qui vient d'être entré. Supposons que vous traitez un fichier dont les lignes sont parfois terminées par un antislash pour indiquer qu'elles continuent sur la ligne suivante. Voici un exemple d'utilisation de redo dans ce cas :

 
Sélectionnez
while (<>) {
    chomp;
    if (s/\\$//) {
        $_ .= <>;
        redo unless eof; # ne pas lire au-delà de la fin de chaque fichier
    }
    # traitement de $_
}

qui est la version Perl usuelle du plus explicite (et laborieux) :

 
Sélectionnez
LIGNE: while (defined($ligne = <ARGV>)) {
    chomp($ligne);
    if ($ligne =~ s/\\$//) {
        $ligne .= <ARGV>;
        redo LIGNE unless eof(ARGV);
    }
    # traitement de $ligne
}

Voici un exemple tiré d'un programme réel qui utilise les trois opérateurs de contrôle de boucle. Bien que cette méthode soit moins courante maintenant que nous disposons des modules Getopt::* dans la distribution de Perl standard, c'est toujours une illustration intéressante de l'utilisation des opérateurs de contrôle de boucle sur des boucles nommées et imbriquées :

 
Sélectionnez
ARG: while (@ARGV && $ARGV[0] =~ s/^-(?=.)//) {
    OPT: for (shift @ARGV) {
        m/^$/       && do {                             next ARG; };
        m/^-$/      && do {                             last ARG; };
        s/^d//      && do { $Niveau_Debug++;            redo OPT; };
        s/^l//      && do { $Genere_Listing++;          redo OPT; };
        s/^i(.*)//  && do { $Sur_Place = $1 || ".bak";  next ARG; };
        say_usage("Option inconnue : $_");
    }
}

Encore un mot au sujet des opérateurs de contrôle de boucle. Vous avez peut-être remarqué que nous ne les appelons pas « instructions ». Ce ne sont en effet pas des instructions — bien que comme toute expression, on puisse les utiliser comme des instructions. Vous pouvez presque les considérer comme des opérateurs unaires qui modifient le déroulement du programme. En fait, vous pouvez même vous en servir là où cela n'a aucun sens. On voit parfois cette erreur de codage :

 
Sélectionnez
open FICHIER, $fichier
    or warn "Impossible d'ouvrir $fichier : $!\n", next FICHIER; # FAUX

L'intention est bonne, mais next FICHIER est analysé comme l'un des paramètres de warn, qui est un opérateur de liste. Donc le next s'exécute avant que le warn ait la moindre chance d'émettre son avertissement. Dans ce cas, cela se corrige facilement en changeant l'opérateur de liste warn en la fonction warn à l'aide de parenthèses bien placées :

 
Sélectionnez
open FICHIER, $fichier
    or warn("Impossible d'ouvrir $fichier : $!\n"), next FICHIER; # Correct

Néanmoins, vous trouverez peut-être ceci plus facile à lire :

 
Sélectionnez
unless (open FICHIER, $fichier) {
    warn "Impossible d'ouvrir $fichier: $!\n";
    next FICHIER;
}

4-5. Blocs simples

Un BLOC (étiqueté ou non) est en soi l'équivalent sémantique d'une boucle qui s'exécute une seule fois. C'est pourquoi vous pouvez utiliser last pour quitter un bloc ou redo pour relancer le bloc.(63) Remarquez que ceci n'est pas le cas des blocs à l'intérieur d'eval {}, de sub {} ou, ce qui en étonne beaucoup, de do {}. En effet, ce ne sont pas des blocs de boucle, car ce ne sont pas des blocs par eux-mêmes. Le mot-clef qui les précède fait d'eux les termes d'expressions qui se trouvent contenir un bloc de code. N'étant pas des blocs de boucle, ils ne peuvent être étiquetés et les contrôles de boucle ne s'y appliquent pas. Les contrôles de boucle ne s'appliquent qu'aux véritables boucles, tout comme return ne s'utilise que dans un sous-programme (ou un eval).

Les contrôles de boucle ne marchent pas non plus avec un if ou un unless, puisque ce ne sont pas des boucles. Mais vous pouvez toujours ajouter une paire d'accolades supplémentaires pour construire un bloc simple qui lui est une boucle :

 
Sélectionnez
if (/pattern/) {{
    last if /alpha/;
    last if /beta/;
    last if /gamma/;
    # ne fait quelque chose que si on se trouve encore dans le if()
}}

Voici comment utiliser un bloc pour faire fonctionner les opérateurs de contrôle de boucle avec une construction do {}. Pour faire un next ou un redo sur un do, ajouter un bloc simple à l'intérieur :

 
Sélectionnez
do {{
    next if $x == $y;
    # faire quelque chose ici
}} until $x++ > $z;

Vous devez être plus subtil pour last :

 
Sélectionnez
{
    do {
        last if $x = $y ** 2;
        # faire quelque chose ici
    } while $x++ <= $z;
}

Et si vous voulez vous servir des deux contrôles de boucle disponibles, vous allez devoir distinguer ces blocs avec des étiquettes :

 
Sélectionnez
DO_LAST:  {
              do {
DO_NEXT:          {
                      next DO_NEXT if $x == $y;
                      last DO_LAST if $x = $y ** 2;
                      # faire quelque chose ici
                  }
              } while $x++ <= $z;
          }

Mais quand vous en arrivez à ce point (ou même avant), vous feriez mieux d'utiliser une simple boucle infinie avec un last à la fin :

 
Sélectionnez
for (;;) {
    next if $x == $y;
    last if $x = $y ** 2;
    # faire quelque chose ici
    last unless $x++ <= $z;
}
4-5-a. Structures de cas

Contrairement à d'autres langages de programmation, Perl n'a pas d'instruction switch ou case officielle. Perl n'en a pas besoin, puisqu'il y a plus d'une manière de faire la même chose. Un bloc simple est particulièrement commode pour construire des structures à choix multiples. En voici une :

 
Sélectionnez
SWITCH: {
    if (/^abc/) { $abc = 1; last SWITCH; }
    if (/^def/) { $def = 1; last SWITCH; }
    if (/^xyz/) { $xyz = 1; last SWITCH; }
    $rien = 1;
}

et une autre :

 
Sélectionnez
SWITCH: {
    /^abc/ && do { $abc = 1; last SWITCH; };
    /^def/ && do { $def = 1; last SWITCH; };
    /^xyz/ && do { $xyz = 1; last SWITCH; };
    $rien = 1;
}

ou, formaté pour que chaque cas ressorte mieux :

 
Sélectionnez
SWITCH: {
    /^abc/      && do {
                       $abc = 1;
                       last SWITCH;
                   };
    /^def/      && do {
                       $def = 1;
                       last SWITCH;
                   };
    /^xyz/      && do {
                       $xyz = 1;
                       last SWITCH;
                   };
    $rien = 1;
}

ou même, horreur :

 
Sélectionnez
if    (/^abc/)  { $abc = 1; }
elsif (/^def/)  { $def = 1; }
elsif (/^xyz/)  { $xyz = 1; }
else            { $rien = 1; }

Remarquez comme dans cet exemple l'opérateur last ignore les blocs do, qui ne sont pas des boucles, et sort de la boucle for :

 
Sélectionnez
for ($nom_de_variable_tres_long[$i++][$j++]->methode()) {
    /ce motif/              and do { push @flags, '-e'; last; };
    /celui-là/              and do { push @flags, '-h'; last; };
    /quelque chose d'autre/ and do {                    last; };
    die "valeur inconnue: `$_'";
}

Vous pouvez trouver bizarre de boucler sur une seule valeur, puisque vous n'allez traverser qu'une fois la boucle, mais il est commode de pouvoir utiliser les capacités d'alias de for/foreach pour affecter $_ temporairement et localement. Cela rend les comparaisons multiples à la même valeur beaucoup plus faciles à taper et il est donc plus difficile de se tromper. On échappe aux effets secondaires d'une nouvelle évaluation de l'expression. Et pour rester en rapport avec cette section, c'est l'un des idiomes les plus répandus pour implémenter une structure de cas.

Pour des cas simples, une cascade d'opérateurs ?: peut également marcher. Ici encore, nous utilisons les capacités d'alias de for afin de rendre les comparaisons multiples plus lisibles :

 
Sélectionnez
for ($couleur_utilisateur) {
    $value = /rouge/ ? 0xFF0000 :
             /vert/  ? 0x00FF00 :
             /bleu/  ? 0x0000FF :
                       0x000000 ;  # noir s'il n'y a plus d'espoir
}

Dans des situations comme celle-ci, il vaut parfois mieux vous construire un hachage et le classer rapidement pour en tirer la réponse. Contrairement aux conditions en cascade que nous avons vues, un hachage peut croître à un nombre illimité d'éléments sans prendre plus de temps pour trouver le premier ou le dernier élément. L'inconvénient est que vous ne pourrez faire que des comparaisons exactes et pas des recherches de motif. Si vous avez un hachage comme celui-ci :

 
Sélectionnez
%couleur = (
    azur        => 0xF0FFFF,
    chartreuse  => 0x7FFF00,
    lavande     => 0xE6E6FA,
    magenta     => 0xFF00FF,
    turquoise   => 0x40E0D0,
);

alors une recherche exacte de chaîne tourne vite :

 
Sélectionnez
$valeur = $couleur{ lc $couleur_utilisateur } || 0x000000;

Même les instructions compliquées de branchement multiple (où chaque cas implique l'exécution de plusieurs instructions différentes) peuvent se transformer en une rapide consultation. Vous avez juste besoin de vous servir d'un hachage de références à des functions. Pour savoir comment les manipuler, voyez la section Hachages de fonctions au chapitre 9, Structures de données.

4-6. goto

Perl supporte aussi un opérateur goto, mais il n'est pas destiné aux œurs sensibles. Il se présente sous trois formes : goto LABEL, goto EXPR et goto &NOM.

La forme goto LABEL trouve l'instruction étiquetée avec LABEL et reprend l'exécution à partir de là. Elle ne peut pas servir à sauter à l'intérieur d'une structure qui nécessite une initialisation, comme un sous-programme ou une boucle foreach. Elle ne peut pas non plus servir pour entrer dans une structure qui a été éliminée lors de l'optimisation (voir le chapitre 18, Compilation). Elle peut servir pour aller à peu près n'importe où dans le bloc courant ou dans un bloc dans votre portée dynamique (c'est-à-dire un bloc duquel vous avez été appelé). Vous pouvez même sortir d'un sous-programme par goto, mais il vaut en général mieux utiliser une autre construction. L'auteur de Perl n'a jamais ressenti le besoin d'utiliser cette forme de goto (en Perl ; en C, c'est une autre affaire).

La forme goto EXPR n'est qu'une généralisation de goto LABEL. Elle attend de l'expression qu'elle produise un nom d'étiquette, dont la position doit évidemment être résolue dynamiquement par l'interpréteur. Cela permet des goto calculés à la FORTRAN, mais n'est pas nécessairement recommandé si vous cherchez à optimiser la maintenabilité du code :

 
Sélectionnez
goto(("TOTO", "TITI", "TUTU")[$i]);     # en espérant que 0 <= i < 3

@loop_label = qw/TOTO TITI TUTU/;
goto $loop_label[rand @loop_label];     # téléportation au hasard

Dans presque tous les cas de ce genre, il est très, très largement préférable d'utiliser les mécanismes structurés de contrôle de flux de next, last ou redo au lieu d'avoir recours à un goto. Pour certaines applications, un hachage de références à des fonctions ou le mécanisme de capture et de gestion d'exceptions s'appuyant sur eval et die sont des approches prudentes.

La forme goto &NAME est fortement magique et suffisamment éloignée du goto usuel pour exempter ceux qui l'utilisent de l'opprobre qui couvre habituellement les utilisateurs de goto. Elle substitue à la routine en cours d'exécution un appel au sous-programme nommé. Ce fonctionnement est utilisé par les routines AUTOLOAD pour charger un autre sous-programme et faire croire que c'est cet autre sous-programme qui était appelé. Après le goto, même caller ne pourra dire que cette routine a été appelée en premier. Les modules autouse, AutoLoader et SelfLoader utilisent tous cette stratégie pour définir les fonctions lorsqu'elles sont appelées pour la première fois puis les lancer sans que personne ne puisse jamais savoir que ces fonctions n'ont pas toujours été là.

4-7. Déclarations globales

Les déclarations de sous-programmes et de formats sont des déclarations globales. Où que vous les placiez, ce qu'elles déclarent est global (c'est local au paquetage, mais comme les paquetages sont globaux au programme, tout ce qui est dans un paquetage est visible de partout). Une déclaration globale peut être placée partout où l'on peut mettre une instruction, mais n'a aucun effet sur l'exécution de la séquence primaire d'instructions, les déclarations prennent effet à la compilation.

Cela signifie que vous ne pouvez pas faire de déclaration conditionnelle de sous-programmes ou de formats et les cacher du compilateur au moyen d'une condition qui ne sera prise en compte qu'à l'exécution. Le compilateur voit les déclarations de sous-programmes et de formats (ainsi que les déclarations use et no) où qu'elles se produisent.

Les déclarations globales sont généralement mises au début ou à la fin de votre programme, ou dans un autre fichier. Cependant si vous déclarez des variables à portée lexicale (voir la section suivante), vous devrez vous assurer que vos déclarations de formats et de sous-programmes sont à portée de vos déclarations de variables si vous espérez accéder à ces variables privées.

Vous avez remarqué que nous sommes sournoisement passés des déclarations aux définitions. Séparer la définition de la déclaration a parfois un intérêt. La seule différence syntaxique entre les deux est que la définition fournit un BLOC contenant le code à exécuter, et pas la déclaration. (Une définition de sous-programme agit comme une déclaration si aucune déclaration n'a été vue.) Séparer la définition de la déclaration vous permet de mettre la déclaration au début du fichier et la définition à la fin (avec vos variables lexicales joyeusement au milieu) :

 
Sélectionnez
sub compte (@);         # Le compilateur sait maintenant comment appeler
                        # compte().
my $x;                  # Le compilateur connaît maintenant la variable
                        # lexicale.
$x = compte(3,2,1);     # Le compilateur peut valider l'appel de fonction.
sub compte (@) { @_ }   # Le compilateur sait maintenant ce que fait compte().

Comme le montre cet exemple, les sous-programmes n'ont pas besoin d'être définis avant que les appels vers eux soient compilés (en fait, leur définition peut même être repoussée jusqu'à leur première utilisation, si vous utilisez l'autochargement), mais la déclaration des sous-programmes aide le compilateur de différentes manières et vous donne plus de choix dans votre façon de les appeler.

La déclaration d'un sous-programme lui permet d'être utilisé sans parenthèses, comme s'il s'agissait d'un opérateur intégré depuis ce point de la compilation. (Nous avons utilisé des parenthèses pour appeler compte dans l'exemple précédent, mais en fait nous n'en avions pas besoin.) Vous pouvez déclarer un sous-programme sans le définir juste en disant :

 
Sélectionnez
sub monnom;
$me = monnom $0         or die "Impossible de trouver mon nom";

Une déclaration simple comme celle-ci déclare la fonction comme un opérateur de liste et non comme un opérateur unaire, aussi faites attention à utiliser or et non || dans ce cas. L'opérateur || lie trop fortement pour être utilisé après des opérateurs de liste, même si vous pouvez toujours mettre des parenthèses autour des arguments de l'opérateur de liste pour le changer en appel de fonction. Autrement, vous pouvez utiliser le prototype ($) pour transformer la routine en opérateur unaire :

 
Sélectionnez
sub monnom ($);
$me = monnom $0         || die "Impossible de trouver mon nom";

C'est maintenant analysé comme vous vous y attendez, mais vous devriez tout de même garder l'habitude d'utiliser or dans cette situation. Pour en savoir plus sur les prototypes, voir le chapitre 6, Sous-programmes.

Vous devez définir le sous-programme à un moment donné, sinon vous obtiendrez une erreur à l'exécution indiquant que vous avez appelé un sous-programme indéfini. À moins de définir le sous-programme vous-même, vous pouvez récupérer les définitions depuis d'autres endroits de plusieurs façons.

Vous pouvez charger les définitions depuis d'autres fichiers avec une simple instruction require ; c'était la meilleure manière de charger des fichiers en Perl 4, mais elle pose deux problèmes. Premièrement, l'autre fichier va typiquement insérer des noms de sous-programmes dans un paquetage (une table de symboles) de son choix, et non vos propres paquetages. Deuxièmement, un require se produit à l'exécution et donc arrive trop tard pour servir de déclaration dans le fichier invoquant le require. Il arrive cependant que vous cherchiez justement à retarder le chargement.

Une manière plus utile de charger les déclarations et les définitions est la déclaration use, qui fait un require du module à la compilation (car use compte comme un bloc BEGIN) et vous laisse importer une partie des déclarations du module dans votre propre programme. On peut donc considérer use comme une déclaration globale en ce qu'elle importe les noms à la compilation dans votre propre paquetage (global) comme si vous les aviez déclarés vous-même. Voir la section Tables de symboles, au chapitre 10, Paquetages au sujet du fonctionnement bas niveau de l'importation entre paquetages, le chapitre 11, Modules, pour savoir comment configurer les paramètres d'importation et d'exportation des modules, et le chapitre 18 pour une explication de BEGIN et de ses cousins CHECK, INIT et END, qui sont aussi des sortes de déclarations globales puisqu'elles sont traitées à la compilation et peuvent avoir un effet global.

4-8. Déclarations avec portée

Comme les déclarations globales, les déclarations à portée lexicale ont un effet au moment de la compilation. Contrairement aux déclarations globales, les déclarations à portée lexicale ne s'appliquent que du point de déclaration jusqu'à la fin du bloc encadrant le plus interne (le premier bloc, fichier ou eval trouvé). C'est pourquoi nous les appelons à portée lexicale, bien que le terme « portée textuelle » soit peut-être plus exact, puisque la portée lexicale n'a rien à voir avec les lexiques. Mais les informaticiens du monde entier savent ce que signifie « portée lexicale », aussi perpétuons-nous cet usage ici.

Perl supporte aussi les déclarations à portée dynamique. Une portée dynamique s'étend aussi jusqu'à la fin du bloc encadrant le plus interne, mais « encadrant » dans ce cas est défini dynamiquement à l'exécution, plutôt que textuellement à la compilation. Pour le dire autrement, les bloc s'emboîtent dynamiquement en invoquant d'autres blocs, pas en les incluant. Cet emboîtement de portées dynamiques peut être corrélé en quelque sorte à l'emboîtement des portées lexicales, mais les deux sont en général différents, particulièrement quand des sous-programmes ont été invoqués.

Nous avons mentionné que certains aspects de use pouvaient être considérés comme des déclarations globales, mais d'autres aspects de use sont à portée lexicale. En particulier, use n'importe pas seulement les symboles, mais implémente également diverses directives de compilation magiques, connues sous le nom de pragmas (ou si vous préférez la forme classique, pragmata). La plupart des pragmas sont à portée lexicale, y compris le pragma use strict 'vars' qui vous oblige à déclarer vos variables avant de vous en servir. Voir plus loin la section Pragmas.

Une déclaration package, curieusement, est elle-même à portée lexicale, malgré le fait qu'un paquetage est une entité globale. Mais une déclaration package se contente de déclarer l'identité du paquetage par défaut pour le reste du bloc l'encadrant. Les noms de variables non déclarés, non qualifiés(64) sont recherchés dans ce paquetage. En un sens, un paquetage n'est jamais déclaré du tout, mais il se met à exister quand vous faites référence à quelque chose qui appartient à ce paquetage. C'est très Perlien.

4-8-a. Déclarations de variables à portée limitée

Le reste de ce chapitre parle surtout de l'utilisation de variables globales. Ou plutôt, parle de la non-utilisation de variables globales. Il existe plusieurs déclarations qui vous aident à ne pas utiliser de variables globales — ou au moins à ne pas les utiliser bêtement.

Nous avons déjà mentionné la déclaration package, qui a été introduite en Perl il y a bien longtemps pour pouvoir séparer les variables globales en paquetages séparés. Cela marche assez bien pour un certain type de variables. Les paquetages sont utilisés par des bibliothèques, des modules et des classes pour stocker leurs données d'interface (et certaines de leurs données semi-privées) pour éviter les conflits avec des variables et des fonctions de même nom dans votre programme principal ou dans d'autres modules. Si vous voyez quelqu'un écrire $Quelque::chose,(65) il se sert de la variable scalaire $chose du paquetage Quelque. Voir le chapitre 10.

Si c'était tout ce qu'il existait en la matière, les programmes Perl deviendraient de plus en plus difficiles à manipuler en grossissant. Heureusement, les trois déclarations de portée de Perl permettent de créer facilement des variables complètement privées (avec my), de donner sélectivement l'accès aux globales (avec our) et de donner des valeurs temporaires à des variables globales (avec local).

 
Sélectionnez
my $nose;
our $House;
local $TV_channel;

Si plusieurs variables sont listées, la liste doit être placée entre parenthèses. Pour my et our, les éléments ne peuvent être que de simples scalaires, tableaux ou hachages. Pour local, les contraintes sont quelque peu relâchées : vous pouvez également localiser des typeglobs en entier ou de simples éléments, ou des tranches de tableaux ou de hachages :

 
Sélectionnez
my ($nose, @eyes, %teeth);
our ($House, @Autos, %Kids);
local (*Spouse, $phone{HOME});

Chacun de ces modificateurs propose une sorte d'isolation différente aux variables qu'il modifie. Pour simplifier légèrement : our confine les noms à une portée, local confine les valeurs à une portée, et my confine à la fois les noms et les valeurs à une portée.

Ces constructions peuvent se voir affecter des valeurs, mais diffèrent en ce qu'elles font réellement avec ces valeurs, puisqu'elles proposent des mécanismes différents de stockage des valeurs. Elles sont aussi quelque peu différentes quand vous ne leur affectez aucune valeur (comme dans notre exemple ci-dessus) : my et local font démarrer les variables en question à la valeur undef ou () appropriée selon le contexte ; our au contraire ne modifie pas la valeur de la variable globale associée.

Syntaxiquement, my, our et local sont simplement des modificateurs (comme des adjectifs) agissant sur une lvalue. Quand vous affectez à une lvalue modifiée, le modificateur ne change pas le fait que la lvalue soit vue comme un scalaire ou une liste. Pour savoir comment le modificateur va fonctionner, faites comme s'il n'était pas là. Ces deux constructions fournissent donc un contexte de liste du côté droit :

 
Sélectionnez
my ($toto)  = <STDIN>;
my @tableau = <STDIN>;

Tandis que celle-ci fournit un contexte scalaire :

 
Sélectionnez
my $toto = <STDIN>;

Les modificateurs lient plus fort (avec une précédence supérieure) que l'opérateur virgule. L'exemple suivant ne déclare qu'une variable au lieu de deux, car la liste qui suit le modificateur n'est pas entre parenthèses.

 
Sélectionnez
my $toto, $titi = 1;        # FAUX

Cela a le même effet que :

 
Sélectionnez
my $toto;
$titi = 1;

Vous serez averti de cette erreur si vous avez demandé les avertissements, avec les options de ligne de commande -w ou -W, ou de préférence avec la déclaration use warnings expliquée plus loin dans la section Pragmas.

En général, il vaut mieux déclarer une variable dans la plus petite portée nécessaire. Comme les variables déclarées dans des instructions de contrôle de flux ne sont visibles que dans le bloc concerné par cette instruction, leur visibilité est réduite. Cela se lit également mieux en anglais (ce qui a moins d'intérêt pour ceux qui codent en français).

 
Sélectionnez
sub verifie_stock {
    for my $machin (our @Inventaire) {
        print "J'ai un $machin en stock aujourd'hui.\n";
    }
}

La forme de déclaration la plus fréquente est my, qui déclare des variables à portée lexicale dont le nom et la valeur sont stockés dans le bloc-note temporaire de la portée en cours et ne peuvent être accédés de manière globale. La déclaration our est très proche de cela, et fait entrer un nom lexical dans la portée en cours, tout comme my, mais pointe en réalité vers une variable globale à laquelle n'importe qui pourrait accéder s'il le voulait. En d'autres termes, c'est une variable globale maquillée en variable lexicale.

L'autre forme de portée, la portée dynamique, s'applique aux variables déclarées avec local, qui malgré l'emploi du mot « local » sont en fait des variables globales qui n'ont rien à voir avec le bloc-notes local.

4-8-b. Variables à portée lexicale : my

Pour vous éviter le casse-tête du suivi des variables globales, Perl fournit des variables à portée lexicale, appelées parfois lexicales pour faire court. Contrairement aux globales, les lexicales vous garantissent le secret de vos variables. Tant que vous ne distribuez pas des références à ces variables privées qui permettraient de les tripatouiller indirectement, vous pouvez être sûr que tous les accès possibles à ces variables privées sont restreints au code contenu dans une section limitée et aisément identifiable de votre programme. Après tout, c'est la raison pour laquelle nous avons choisi le mot-clef my.

Une séquence d'instructions peut contenir des déclarations de variables à portée lexicale. De telles déclarations sont habituellement placées au début de la séquence d'instructions, mais ce n'est pas une obligation. En plus de déclarer les noms de variables à la compilation, les déclarations fonctionnent comme des instructions normales à l'exécution : chacune d'entre elles est élaborée dans la séquence d'instructions comme s'il s'agissait d'une instruction usuelle sans le modificateur :

 
Sélectionnez
my $nom = "fred";
my @affaires = ("voiture", "maison", "marteau");
my ($vehicule, $domicile, $outil) = @affaires;

Ces variables lexicales sont totalement cachées du monde à l'extérieur de la portée les contenant immédiatement. Contrairement aux effets de portée dynamique de local (voir la section suivante), les lexicales sont cachées à tout sous-programme appelé depuis leur portée. Cela reste vrai même si le même sous-programme est appelé depuis lui-même ou ailleurs — chaque instance du sous-programme possède son propre « bloc-notes » de variables lexicales.

Contrairement aux portées de blocs, les portées de fichiers ne s'emboîtent pas ; il ne se produit pas d'« inclusion », tout au moins pas textuellement. Si vous chargez du code d'un autre fichier avec do, require ou use, le code contenu dans ce fichier ne pourra pas accéder à vos variables lexicales, tout comme vous ne pourrez pas accéder aux siennes.

Cependant toute portée à l'intérieur d'un fichier (ou même le fichier lui-même) fait l'affaire. Il est souvent utile d'avoir des portées plus larges que la définition de sous-programme, car cela vous permet de partager des variables privées avec un nombre limité de routines. C'est ainsi que vous créez des variables qu'un programmeur C appellerait « statiques » :

 
Sélectionnez
{
    my $etat = 0;
    sub on { $etat = 1 }
    sub off { $etat = 0 }
    sub change { $etat = !$etat }
}

L'opérateur eval CHAINE fonctionne aussi comme une portée emboîtée, puisque le code à l'intérieur de l'eval peut voir les variables lexicales de l'appelant (tant que leurs noms ne sont pas cachés par des déclarations identiques dans la portée définie par l'eval lui-même). Les routines anonymes peuvent de même accéder à n'importe quelle variable lexicale des portées les renfermant ; si elles le font, elles sont alors nommées fermetures.(66) En combinant ces deux notions, si un bloc evalue une chaîne qui crée un sous-programme anonyme, ce sous-programme devient une fermeture avec accès complet aux lexicales de l'eval et du bloc, même après que le l'eval et le bloc se sont terminés. Voir la section Fermetures au chapitre 8.

La variable nouvellement déclarée (ou la valeur, dans le cas de local) n'apparaît pas avant la fin de l'instruction suivant l'instruction contenant la déclaration. Vous pourriez donc copier une variable de cette façon :

 
Sélectionnez
my$x = $x;

Cela initialise le nouveau $x interne avec la valeur courante de $x, que la signification de $x soit globale ou lexicale. (Si vous n'initialisez pas la nouvelle variable, elle démarre avec une valeur vide ou indéfinie.)

La déclaration d'une variable lexicale d'un nom donné cache toute variable lexicale du même nom déclarée précédemment. Elle cache aussi toute variable globale non qualifiée du même nom, mais celle-ci reste toujours accessible en la qualifiant explicitement avec le nom du paquetage la contenant, par exemple $NomPaquetage::nomvar.

4-8-c. Déclarations de globales à portée lexicale : our

La déclaration our est une meilleure manière d'accéder aux globales, en particulier pour les programmes et les modules tournant avec la déclaration use strict. Cette déclaration est à portée lexicale dans le sens où elle ne s'applique que jusqu'à la fin de la portée courante. Mais contrairement au my à portée lexicale ou au local à portée dynamique, our n'isole rien dans la portée lexicale ou dynamique en cours. En fait, il donne accès à une variable globale dans le paquetage en cours, en cachant les lexicales de même nom qui vous auraient sinon empêché de voir cette globale. À cet égard, nos variables our fonctionnent tout comme mes variables my.

Si vous placez une déclaration our en dehors de tout bloc délimité par des accolades, elle dure jusqu'à la fin de l'unité de compilation en cours. Souvent, on la place juste au début de la définition d'un sous-programme pour indiquer qu'il accède à une variable globale :

 
Sélectionnez
sub verifie_entrepot {
    our @Inventaire_Courant;
    my $machin;
    foreach $machin (@Inventaire_Courant) {
        print "J'ai un $machin en stock aujourd'hui.\n";
    }
}

Comme les variables globales ont une durée de vie plus longue et une visibilité plus large que les variables privées, nous préférons utiliser pour elles des noms plus longs et plus voyants que pour les variables temporaires. Cette simple habitude, si elle est suivie consciencieusement, peut faire autant que use strict pour décourager l'emploi de variables globales, particulièrement chez ceux qui ne sont pas des virtuoses du clavier.

Des déclarations our répétées ne s'emboîtent pas clairement. Chaque my emboîté produit une nouvelle variable, et chaque local emboîté produit une nouvelle valeur. Mais à chaque fois que vous employez our, vous mentionnez la même variable globale, sans notion d'emboîtement. Quand vous affectez à une variable our, les effets de cette affectation persistent une fois hors de portée de la déclaration. C'est parce que our ne crée jamais de valeur ; il donne seulement une forme d'accès limité à la globale qui, elle, existe pour toujours :

 
Sélectionnez
our $NOM_PROGRAMME = "client";
{
    our $NOM_PROGRAMME = "serveur";
    # Le code appelé d'ici voit "serveur".
    ...
}
# Le code exécuté ici voit toujours "serveur".

Comparez ceci avec ce qui arrive avec my ou local, quand la variable ou la valeur redevient visible après le bloc :

 
Sélectionnez
my $i = 10;
{
    my $i = 99;
    ...
}
# Le code compilé ici voit la variable externe.

local $NOM_PROGRAMME = "client";
{
    local $NOM_PROGRAMME = "serveur";
    # Le code appelé d'ici voit "serveur".
    ...
}
# Le code exécuté ici voit "client" de nouveau.

Faire une déclaration our n'a en général de sens qu'une seule fois, probablement tout au début de votre programme ou module ou, plus rarement, quand vous préfixez le our de son propre local :

 
Sélectionnez
{
    local our @Inventaire_Courant = qw(bananes);
    verifie_entrepot(); # non, nous n'avons pas de bananes :-)
}
4-8-d. Variables à portée dynamique : local

L'utilisation de l'opérateur local sur une variable globale lui donne une valeur temporaire à chaque fois que local est exécuté, mais n'affecte pas la visibilité globale de cette variable. Quand le programme atteint la fin de cette portée dynamique, cette valeur temporaire est jetée et la valeur précédente restaurée. Mais pendant que ce bloc s'exécute, c'est toujours une variable globale qui se trouve juste contenir une valeur temporaire. Si vous appelez une autre fonction pendant que votre variable globale contient la valeur temporaire et que cette fonction accède à cette variable globale, elle verra la valeur temporaire et pas la valeur initiale. En d'autres termes, cette autre fonction est dans votre portée dynamique, même si elle n'est probablement pas dans votre portée lexicale.(67)

Si vous avez un local qui ressemble à ceci :

 
Sélectionnez
{
    local $var = $nouveau;
    ma_fonction();
    ...
}

vous pouvez le voir entièrement en termes d'affectations à l'exécution :

 
Sélectionnez
{
    $ancien = $var;
    local $var = $nouveau;
    ma_fonction();
    ...
}
continue {
    $var = $ancien;
}

La différence est qu'avec local, la valeur est restaurée quelle que soit la manière dont vous quittez le bloc, même si vous sortez de cette portée en faisant un return prématuré. La variable est toujours la même variable globale, mais la valeur qui s'y trouve dépend de la portée d'où la fonction a été appelée. C'est pourquoi on l'appelle portée dynamique : parce qu'elle change au cours de l'exécution.

Comme avec my, vous pouvez initialiser un local avec une copie de la même variable globale. Toutes les modifications faites à cette variable pendant l'exécution du sous-programme (et de tous les autres appelés depuis celui-ci, qui peuvent bien sûr voir cette globale à portée dynamique) seront perdues quand la routine se terminera. Vous devriez sûrement commenter ce que vous faites :

 
Sélectionnez
# ATTENTION : les modifications sont temporaires
# pour cette portée dynamique
local $Ma_Globale = $Ma_Globale;

Une variable globale est donc toujours visible dans l'intégralité de votre programme, qu'elle ait été déclarée avec our, qu'elle ait été créée à la volée ou qu'elle contienne une valeur locale destinée à être jetée une fois hors de portée. Ce n'est pas compliqué pour de petits programmes. Mais vous allez rapidement ne plus retrouver où sont utilisées ces variables globales dans de plus gros programmes. Si vous voulez, vous pouvez interdire l'utilisation accidentelle de variables globales à l'aide du pragma use strict 'vars', décrit à la section suivante.

Bien que my et local confèrent toutes deux un certain niveau de protection, vous devriez largement préférer my à local. Cependant, vous devrez de temps en temps utiliser local pour pouvoir modifier temporairement la valeur d'une variable globale existante, comme celles décrites au chapitre 28, Noms spéciaux. Seuls les identificateurs alphanumériques peuvent avoir une portée lexicale et beaucoup de ces variables spéciales ne sont pas strictement alphanumériques. Vous aurez aussi besoin de local pour faire des modifications temporaires à la table des symboles d'un paquetage, comme c'est décrit dans la section Tables de symboles au chapitre 10. Enfin, vous pouvez aussi utiliser local sur un élément isolé ou toute une tranche d'un tableau ou d'un hachage. Cela fonctionne même si le tableau ou le hachage est en fait une variable lexicale, en rajoutant la couche de comportement de portée dynamique de local au-dessus de ces variables lexicales. Nous ne parlerons pas plus de la sémantique de local ici. Pour plus d'informations, voir local au chapitre 29.

4-9. Pragmas

De nombreux langages de programmation vous permettent de donner des directives au compilateur. En Perl ces directives sont passées au compilateur par la déclaration use. Certains de ces pragmas sont :

 
Sélectionnez
use warnings;
use strict;
use integer;
use bytes;
use constant pi => ( 4 * atan2(1,1) );

Les pragmas de Perl sont décrits au chapitre 31, Modules de pragmas, mais nous allons tout de suite parler des plus utiles par rapport au contenu de ce chapitre.

Bien que certains soient des déclarations qui affectent les variables globales ou le paquetage en cours, la plupart des pragmas sont des déclarations à portée lexicale dont les effets ne durent que jusqu'à la fin du bloc, du fichier ou de l'eval qui les contient (en fonction du premier qui se présente). Un pragma à portée lexicale peut être annulé dans une portée incluse avec une déclaration no, qui fonctionne exactement comme use, mais à l'envers.

4-9-a. Contrôle des avertissements

Pour vous montrer comment tout cela fonctionne, nous allons manipuler le pragma warnings pour dire à Perl s'il doit nous avertir de pratiques discutables :

 
Sélectionnez
use warnings;       # Permet les avertissements d'ici à la fin de fichier
...
{
    no warnings;    # Désactive les avertissements dans le bloc
    ...
}
# Les avertissements sont automatiquement réactivés ici

Une fois les avertissements demandés, Perl vous signalera les variables utilisées une seule fois, les déclarations qui cachent d'autres déclarations dans la même portée, les conversions incorrectes de chaînes en nombres, l'utilisation de valeurs indéfinies comme des nombres ou des chaînes normaux, les tentatives d'écriture sur des fichiers ouverts en lecture seule (ou pas ouverts du tout) et bien d'autres problèmes potentiels listés au chapitre 33, Messages de diagnostic.

La méthode de contrôle des avertissements recommandée est l'utilisation de use warnings. Les anciens programmes ne pouvaient utiliser que l'option de ligne de commande -w ou bien modifier la variable globale $^W :

 
Sélectionnez
{
    local $^W = 0;
    ...
}

Il vaut beaucoup mieux utiliser les pragmas use warnings et no warnings. Un pragma vaut mieux, car il se produit à la compilation, parce que c'est une déclaration lexicale et ne peut donc pas affecter du code qu'il n'était pas censé affecter, et (bien que nous ne vous l'ayons pas montré dans ces simples exemples) qu'il permet un contrôle plus fin sur plusieurs ensembles de classes. Pour en savoir plus sur le pragma warnings, y compris comment transformer des avertissements qui font juste un peu de bruit en erreurs fatales et comment supplanter le pragma pour activer les avertissements même si le module ne veut pas, voir use warnings au chapitre 31.

4-9-b. Contrôle de l'utilisation des globales

Une autre déclaration communément rencontrée est le pragma use strict, qui a plusieurs fonctions dont celle de contrôler l'utilisation des variables globales. Normalement, Perl vous laisse créer de nouvelles variables (ou trop souvent, écraser d'anciennes variables) simplement en les citant. Aucune déclaration de variable n'est nécessaire (par défaut). Sachant que l'utilisation débridée de globales peut rendre les gros programmes ou les gros modules pénibles à maintenir, vous pourrez vouloir décourager leur utilisation accidentelle. Pour prévenir de tels accidents, vous pouvez écrire :

 
Sélectionnez
use strict 'vars';

Cela signifie que toute variable mentionnée à partir d'ici jusqu'à la fin de la portée courante doit faire référence soit à une variable lexicale, soit à une variable globale explicitement autorisée. Si ce n'est pas le cas, une erreur de compilation se produit. Une variable globale est explicitement autorisée si l'une de ces propositions est vraie :

  • C'est l'une des variables spéciales de Perl (voir le chapitre 28).
  • Elle est complètement définie avec son nom de paquetage (voir le chapitre 10).
  • Elle a été importée dans le paquetage courant (voir le chapitre 11).
  • Elle se fait passer pour une variable lexicale à l'aide d'une déclaration our. (C'est la raison principale pour laquelle nous avons ajouté la déclaration our à Perl.)

Bien sûr, il reste toujours la cinquième possibilité. Si le pragma est trop exigeant, annulez-le simplement dans un bloc intérieur avec :

 
Sélectionnez
no strict 'vars'

Avec ce pragma vous pouvez aussi demander une vérification stricte des déréférencements symboliques et de l'utilisation des mots simples. Habituellement, les gens tapent juste :

 
Sélectionnez
use strict;

pour mettre en œuvre les trois restrictions. Pour plus d'informations, voir l'entrée use strict, au chapitre 31.

5. Recherche de motif

Le support intégré de Perl pour la recherche de motif vous permet de rechercher dans de grandes quantités de données simplement et efficacement. Que vous fassiez tourner un énorme site portail commercial balayant tous les groupes de news existants à la recherche de potins intéressants, un organisme public occupé à comprendre la démographie humaine (ou le génome humain), une institution scolaire désireuse de mettre des informations dynamiques sur votre site web, Perl est l'outil qu'il vous faut ; d'une part à cause de ses liens avec les bases de données, mais surtout à cause de ses capacités de recherche de motif. Si vous prenez le mot « texte » dans son sens le plus large, près de 90 % de tout ce vous faites est du traitement de texte. C'est vraiment ce pour quoi Perl est fait et a toujours été fait — en fait, cela fait même partie de son nom : Practical Extraction and Report Language. Les motifs de Perl fournissent de puissants moyens pour ratisser des montagnes de données brutes et en extraire de l'information utile.

Vous spécifiez un motif en créant une expression rationnelle (ou expression régulière(68) ou regex) et le moteur d'expressions régulières de Perl (le « Moteur », pour le reste de ce chapitre) prend cette expression et détermine si (et comment) le motif correspond à vos données. Alors que la plupart de vos données seront probablement des chaînes de caractères, rien ne vous empêche de vous servir des regex pour rechercher et remplacer n'importe quelle séquence de bits, y compris ce que vous auriez cru être des données « binaires ». Pour Perl, les octets sont juste des caractères qui ont une valeur ordinale inférieure à 256. (Pour en savoir plus sur ce sujet, voir le chapitre 15, Unicode.)

Si vous connaissiez déjà les expressions régulières avec d'autres outils, nous devons vous prévenir que celles de Perl sont un peu différentes. Premièrement elles ne sont pas vraiment « régulières » (ou « rationnelles ») au sens théorique du mot, ce qui signifie qu'elles peuvent faire beaucoup plus que les expressions rationnelles qu'on apprend en cours d'informatique. Deuxièmement, elles sont tellement utilisées en Perl, qu'elles ont leurs propres variables spéciales et leurs propres conventions de citation qui sont étroitement intégrées au langage, pas vaguement liées comme n'importe quelle autre librairie. Les nouveaux programmeurs Perl cherchent souvent en vain des fonctions comme :

 
Sélectionnez
match( $chaine, $motif );
subst( $chaine, $motif, $remplacement );

Mais la recherche et la substitution sont deux tâches si fondamentales en Perl qu'elles méritent leurs propres opérateurs d'une lettre : m/MOTIF/ et s/MOTIF/REMPLACEMENT/. Ils sont non seulement brefs syntaxiquement, mais ils sont aussi analysés comme des chaînes entre apostrophes doubles plutôt que comme des opérateurs ordinaires ; toutefois, ils fonctionnent comme des opérateurs, c'est pourquoi nous les appelons ainsi. Tout au long de ce chapitre, vous les verrez utilisés pour comparer des motifs à des chaînes. Si une partie de la chaîne correspond au motif, nous disons que la correspondance (ou recherche) est réussie. Il y a plein de trucs sympas à faire avec des correspondances réussies. En particulier, si vous utilisez s///, une correspondance réussie provoque le remplacement de la partie correspondante dans la chaîne parce que vous avez spécifié comme REMPLACEMENT.

Tout ce chapitre concerne la construction et l'utilisation de motifs. Les expressions régulières de Perl sont puissantes, et concentrent beaucoup de sens en peu de volume. Elles peuvent donc vous intimider si vous essayez de saisir le sens d'un long motif d'un seul coup. Mais si vous pouvez le découper en petits morceaux et que vous savez comment le Moteur interprète ces morceaux, vous pouvez comprendre n'importe quelle expression régulière. Il n'est pas rare de voir une centaine de lignes de code C ou Java exprimées en une expression régulière d'une ligne en Perl. Cette expression régulière est peut-être un peu plus difficile à comprendre que n'importe quelle ligne du gros programme ; d'un autre côté, la regex sera beaucoup plus facile à comprendre que le plus long programme pris dans son ensemble. Il vous faut juste garder tout cela en perspective.

5-1. Bestiaire des expressions régulières

Avant de nous plonger dans les règles d'interprétation des expressions régulières, regardons à quoi certains motifs ressemblent. La plupart des caractères d'une expression régulière se correspondent à eux-mêmes. Si vous enchaînez plusieurs caractères à la suite, ils se correspondent dans l'ordre, comme on s'y attend. Donc si vous écrivez la recherche suivante :

 
Sélectionnez
/Frodon/

vous pouvez être sûr que le motif ne correspondra que si la chaîne contient quelque part la sous-chaîne « Frodon ». (Une sous-chaîne est juste un morceau de chaîne.) La correspondance peut se faire n'importe où dans la chaîne, tant que ces six caractères apparaissent quelque part, l'un après l'autre et dans cet ordre.

D'autres caractères ne se correspondent pas à eux-mêmes, mais se comportent d'une étrange manière. Nous les appelons métacaractères. (Tous les métacaractères sont des coquins, mais certains sont si mauvais, qu'ils conduisent les caractères voisins à se comporter aussi mal qu'eux.)

Voici les scélérats :

 
Sélectionnez
\|()[ { ^ $ * + ? .

Les métacaractères sont en fait très utiles et ont une signification spéciale à l'intérieur des motifs. Nous vous expliquerons toutes ces significations au fur et à mesure de ce chapitre. Mais sachez d'abord que vous pourrez toujours détecter n'importe lequel de ces douze caractères en le faisant précéder d'un antislash. Par exemple, comme l'antislash est un métacaractère, pour détecter un antislash, vous devrez l'antislasher : \\.

Vous voyez, l'antislash est le genre de caractère qui pousse les autres caractères à mal se conduire. Finalement, quand vous faites mal se conduire un métacaractère indiscipliné, il se conduit bien — un peu comme une double négation. Antislasher un caractère pour le prendre littéralement marche, mais seulement sur les caractères de ponctuation ; antislasher un caractère alphanumérique (qui habituellement se comporte correctement) fait le contraire : cela transforme le caractère littéral en quelque chose de spécial. Dès que vous voyez une telle séquence de deux caractères :

 
Sélectionnez
\b\D \t\3 \s

vous saurez que la séquence est un métasymbole qui correspond à quelque chose d'étrange. Par exemple, \b correspond à une limite de mot, tandis que \t correspond à un caractère de tabulation ordinaire. Remarquez qu'une tabulation fait un caractère de large, alors que la limite de mot a une largeur de zéro caractère, puisque c'est un point entre deux caractères. Nous appellerons donc \b une assertion de largeur nulle. Cependant, \t et \b se ressemblent en ce qu'ils supposent quelque chose sur une caractéristique de la chaîne. À chaque fois que vous faites une assertion au sujet de quelque chose dans une expression régulière, vous demandez à ce que quelque chose en particulier soit vrai pour que le motif corresponde.

La plupart des éléments d'une expression régulière sont des assertions, y compris les caractères ordinaires qui veulent se correspondre à eux-mêmes. Pour être plus précis, ils supposent aussi que le prochain élément sera en correspondance un caractère plus loin dans la chaîne, c'est pourquoi nous disons que la tabulation est de « largeur un caractère ». Certaines assertions (comme \t) consomment un peu de la chaîne au fur et à mesure qu'ils entrent en correspondance et d'autres (comme \b) non. Mais nous réservons en général le terme « assertion » pour les assertions de largeur nulle. Pour éviter les confusions, nous appellerons celles qui ont une largeur des atomes. (Si vous êtes physicien, vous pouvez voir les atomes de largeur non nulle comme des particules massives, à la différence des assertions, qui n'ont pas de masse comme les photons.)

Vous allez voir aussi des métacaractères qui ne sont pas des assertions ; ils sont plutôt structurels (tout comme les accolades et les points-virgules définissent la structure du code Perl, mais ne font pas vraiment quelque chose). Ces métacaractères structurels sont d'une certaine manière les plus importants, car le premier pas dans votre apprentissage de la lecture des expressions régulières est d'habituer votre regard à reconnaître les métacaractères structurels. Une fois que vous avez appris cela, lire des expressions régulières est un jeu d'enfant(69).

L'un de ces métacaractères structurels est la barre verticale, qui indique un choix :

 
Sélectionnez
/Frodon|Pippin|Merry|Sam/

Cela signifie que n'importe laquelle de ces chaînes peut correspondre. Ceci est traité dans la section Alternative plus loin dans ce chapitre. Dans la partie Capture et regroupement, nous vous montrerons comment utiliser les parenthèses autour de morceaux de votre motif pour faire des regroupements :

 
Sélectionnez
/(Frodon|Drogon|Bilbon) Sacquet/

ou même :

 
Sélectionnez
/(Frod|Drog|Bilb)on Sacquet/

Une autre chose que vous verrez sont les quantificateurs, qui disent combien de fois ce qui précède doit être trouvé à la queue leu leu. Les quantificateurs ressemblent à ceci :

 
Sélectionnez
* + ? *? {3} {2,5}

Vous ne les verrez cependant jamais isolément. Les quantificateurs n'ont de sens qu'attachés à des atomes — c'est-à-dire à des assertions qui ont une largeur non nulle.(70) Les quantificateurs s'attachent à l'atome précédent seulement. Ce qui signifie en termes clairs qu'ils ne quantifient normalement qu'un seul caractère. Si vous voulez une correspondance avec trois copies de « bar » à la suite, vous devez regrouper les caractères individuels de « bar » dans une seule « molécule » avec des parenthèses, comme ceci :

 
Sélectionnez
/(bar){3}/

Cela correspondra avec « barbarbar ». Si vous aviez écrit /bar{3}/, cela aurait correspondu avec « barrr » — qui aurait sonné écossais, mais n'aurait pas constitué un barbarbarisme. Pour en savoir plus sur les quantificateurs, voir plus loin la section Quantificateurs.

Maintenant que vous avez rencontré quelques-unes des bestioles qui habitent les expressions régulières, vous êtes sûrement impatient de commencer à les apprivoiser. Cependant, avant que nous ne discutions sérieusement des expressions régulières, nous allons un petit peu rebrousser chemin et parler des opérateurs qui utilisent les expressions régulières. (Et si vous apercevez quelques nouvelles bestioles regex en chemin, n'oubliez pas le guide.)

5-2. Opérateurs de recherche de motifs

Zoologiquement parlant, les opérateurs de recherche de motif de Perl fonctionnent comme une sorte de cage à expressions régulières : ils les empêchent de sortir. Si nous laissions les regex errer en liberté dans le langage, Perl serait une jungle complète. Le monde a besoin de jungles bien sûr — après tout, ce sont les moteurs de la diversité biologique —, mais les jungles devraient rester où elles sont. De même, bien qu'étant le moteur de la diversité combinatoire, les expressions régulières devraient rester à l'intérieur des opérateurs de recherche de motifs où est leur place. C'est une jungle, là-dedans.

Comme si les expressions régulières n'étaient pas assez puissantes, les opérateurs m// et s/// leur donnent le pouvoir (lui aussi confiné) de l'interpolation entre apostrophes doubles. Comme les motifs sont analysés comme des chaînes entre apostrophes doubles, toutes les conventions usuelles des apostrophes doubles fonctionnent, y compris l'interpolation de variables (à moins que vous n'utilisiez les apostrophes simples comme délimiteurs) et les caractères spéciaux indiqués par des séquences d'échappement avec antislash. (Voir Caractères spécifiques plus loin dans ce chapitre.) Celles-ci sont appliquées avant que la chaîne soit interprétée comme une expression régulière. (C'est l'un des quelques endroits de Perl où une chaîne subit un traitement en plusieurs passes.) La première passe n'est pas une interprétation entre apostrophes doubles tout à fait normale, en ce qu'elle sait ce qu'elle doit interpoler et ce qu'elle doit passer à l'analyseur d'expressions régulières. Donc par exemple, tout $ immédiatement suivi d'une barre verticale, d'une parenthèse fermante ou de la fin de chaîne ne sera pas traité comme une interpolation de variable, mais comme la traditionnelle assertion de regex qui signifie fin-de-ligne. Donc si vous écrivez :

 
Sélectionnez
$toto = "titi";
/$toto$/;

la passe d'interpolation entre apostrophes doubles sait que ces deux $ fonctionnent différemment. Elle fait l'interpolation de $toto puis envoie ceci à l'analyseur d'expressions régulières :

 
Sélectionnez
/titi$/;

Une autre conséquence de cette analyse en deux passes est que le tokener ordinaire de Perl trouve la fin de l'expression régulière en premier, tout comme s'il cherchait le délimiteur final d'une chaîne ordinaire. C'est seulement après avoir trouvé la fin de la chaîne (et fait les interpolations de variables) que le motif est traité comme une expression régulière. Entre autres choses, cela signifie que vous ne pouvez pas « cacher » le délimiteur final d'un motif à l'intérieur d'un élément de regex (comme une classe de caractères ou un commentaire de regex, que nous n'avons pas encore couvert). Perl verra le délimiteur où qu'il se trouve et terminera le motif à cet endroit.

Vous devez également savoir que l'interpolation de variables dans un motif ralentit l'analyseur de motif, car il se sent obligé de vérifier si la variable a été modifiée, au cas où il aurait à recompiler le motif (ce qui le ralentira encore plus). Voir la section Interpolation de variables plus loin dans ce chapitre.

L'opérateur de translittération tr/// ne fait pas d'interpolation de variables ; il n'utilise même pas d'expressions régulières ! (En fait, il ne devrait probablement pas faire partie de ce chapitre, mais nous n'avons pas trouvé de meilleur endroit où le mettre.) Il a cependant une caractéristique commune avec m// et s/// : il se lie aux variables grâce aux opérateurs =~ et !~.

Les opérateurs =~ et !~, décrits au chapitre 3, Opérateurs unaires et binaires, lient l'expression scalaire à leur gauche à l'un des trois opérateurs de type apostrophes (ou guillemets) à leur droite : m// pour rechercher un motif, s/// pour substituer une chaîne à une sous-chaîne correspondant au motif et tr/// (ou son synonyme, y///) pour traduire un ensemble de caractères en un autre ensemble de caractères. (Vous pouvez écrire // pour m// sans le m si les slash sont utilisés comme délimiteurs.) Si la partie à droite de =~ ou !~ n'est aucune de ces trois-là, elle compte toujours comme une opération m/// de recherche, mais il n'y a pas la place de mettre un seul modificateur final (voir plus loin la section Modificateurs de motif) et vous devrez gérer vos propres apostrophes :

 
Sélectionnez
print "correspond" if $unechaine =~ $unmotif;

Vraiment, il n'y a pas de raison de ne pas l'écrire explicitement :

 
Sélectionnez
print "correspond" if $unechaine =~ m/$unmotif/;

Quand ils sont utilisés pour une recherche de correspondance, =~ et !~ se prononcent parfois respectivement « correspond à » et « ne correspond pas à » (bien que « contient » et « ne contient pas » puissent être plus clair).

Hormis les opérateurs m// et s///, les expressions régulières apparaissent à deux autres endroits dans Perl. Le premier argument de la fonction split est une forme spéciale de l'opérateur de correspondance spécifiant les parties à ne pas retourner quand on découpe une chaîne en plusieurs sous-chaînes. Voir la description de split et les exemples au chapitre 29, Fonctions. L'opérateur qr// (quote regex) spécifie également un motif à l'aide d'une regex, mais il n'essaie pas de faire une correspondance avec quoi que ce soit (contrairement à m//, qui le fait). Au lieu de cela, il retourne une forme compilée de la regex pour une utilisation ultérieure. Voir Interpolation de variables pour plus d'informations.

Vous appliquez l'un des opérateurs m//, s/// ou tr/// à une chaîne particulière avec l'opérateur de lien =~ (qui n'est pas un véritable opérateur, mais plutôt un indicateur du sujet de l'opération). Voici quelques exemples :

 
Sélectionnez
$meuledefoin =~ m/aiguille/          # cherche un motif simple
$meuledefoin =~ /aiguille/           # pareil

$italiano =~ s/beurre/huile d'olive/ # une substitution bonne pour la santé

$rotate13 =~ tr/a-zA-Z/n-za-mN-ZA-M/ # encryption simple (à casser)

Sans opérateur de lien, c'est $_ qui est implicitement utilisé comme "sujet" :

 
Sélectionnez
/nouvelles vies/ and        # explore $_ et (si on trouve quelque chose)
    /autres civilisations/  # au mépris du danger, explore encore $_

s/sucre/aspartame/          # substitue un substitut dans $_

tr/ATCG/TAGC/               # complémente le brin d'ADN dans $_

Comme s/// et tr/// modifient le scalaire auquel ils sont appliqués, vous ne pouvez les utiliser qu'avec des lvalues valides :

 
Sélectionnez
"onshore" =~ s/on/off/;  # FAUX : erreur à la compilation

En revanche, m// fonctionne sur le résultat de n'importe quelle expression scalaire :

 
Sélectionnez
if ((lc $chapeau_magique->contenu->comme_chaine) =~ /lapin/) {
    print "Euh, quoi d'neuf, docteur ?\n";
}
else {
    print "Ce tour ne marche jamais !\n";
}

Mais vous devez être un petit peu prudent, car =~ et !~ ont une précédence assez élevée (dans notre exemple précédent, les parenthèses sont nécessaires autour du terme de gauche).(71) L'opérateur de lien !~ fonctionne comme =~, mais inverse la logique du résultat de l'opération :

 
Sélectionnez
if ($romance !~ /parole/) {
    print qq/"$romance" semble être une romance sans parole.\n/;
}

Comme m//, s/// sont des opérateurs apostrophes (ou guillemets), vous pouvez choisir vos délimiteurs. Ils fonctionnent de la même façon que les opérateurs de citation q//, qq//, qr//, et qw// (voir la section Choisissez vos délimiteurs, chapitre 2, Composants de Perl).

 
Sélectionnez
$path =~ s#/tmp#/var/tmp/scratch#;

if ($dir =~ m[/bin]) {
    print "Pas de répertoires de binaires, s'il vous plait.\n";
}

Quand vous utilisez des délimiteurs appariés avec s/// ou tr///, si la première partie utilise l'une des quatre paires usuelles d'encadrement (parenthèses, crochets, accolades ou chevrons), vous pouvez choisir des délimiteurs différents des premiers pour la seconde partie :

 
Sélectionnez
s(oeuf)<larve>;
s{larve}{chrysalide};
s[chrysalide]/imago/;

Les blancs sont autorisés avant les délimiteurs ouvrants :

 
Sélectionnez
s (oeuf)       <larve>;
s {larve}      {chrysalide};
s [chrysalide] /imago/;

À chaque fois qu'un motif correspond avec succès (y compris les motifs de substitution), il affecte aux variables $`, $& et $' le texte à gauche de la correspondance, le texte correspondant et le texte à droite de la correspondance. C'est utile pour séparer les chaînes en leurs éléments :

 
Sélectionnez
"pain au chocolat" =~ /au/;
print "Trouvé :         <$`> $& <$'>\n"; # Trouvé : <pain > au < chocolat>
print "Gauche :         <$`>\n";         # Gauche :         <pain >
print "Correspondance : <$&>\n";         # Correspondance : <au>
print "Droite :         <$'>\n";         # Droite :         < chocolat>

Pour plus d'efficacité et de contrôle, utilisez des parenthèses pour capturer les portions spécifiques que vous voulez conserver. Chaque paire de parenthèses capture la sous-chaîne correspondant au sous-motif entre parenthèses. Les paires de parenthèses sont numérotées de gauche à droite par la position de chaque parenthèse ouvrante. Une fois la correspondance établie, les sous-chaînes correspondantes sont disponibles dans les variables numérotées $1, $2, $3 et ainsi de suite :(72)

 
Sélectionnez
$_ = "Bilbon Sacquet est né le 22 Septembre";
/(.*) est né le (.*)/;
print "Personne: $1\n";
print "Date: $2\n";

$`, $&, $' et les variables numérotées sont implicitement localisées dans la portée dynamique les contenant. Elles durent jusqu'à la prochaine recherche réussie ou jusqu'à la fin de la portée courante, selon ce qui arrive en premier. Nous en reparlerons plus tard, dans un cadre différent.

Une fois que Perl a détecté que vous aurez besoin de l'une des variables $`, $& ou $' quelque part dans votre programme, il les fournira pour chaque correspondance. Cela va ralentir un peu votre programme. Comme Perl utilise un mécanisme semblable pour produire $1, $2 et les autres, pour chaque motif contenant des parenthèses de capture vous payez un peu en performance. (Voir Regroupement pour éviter le coût de la capture en conservant la possibilité de regrouper.) Si vous n'utilisez jamais $`, $& ou $', alors les motifs sans parenthèses ne seront pas pénalisés. Si vous pouvez, il vaut donc mieux en général éviter d'utiliser $`, $& et $', en particulier dans les modules de bibliothèque. Mais si vous devez les utiliser au moins une fois (et certains algorithmes apprécient vraiment leur commodité), alors utilisez-les autant que vous voulez, car vous avez déjà payé le prix. $& n'est pas aussi coûteux que les deux autres dans les versions récentes de Perl.

5-2-a. Modificateurs de motif

Nous allons discuter des différents opérateurs de recherche de motif dans un moment, mais nous aimerions d'abord parler d'un de leurs points communs à tous : les modificateurs.

Vous pouvez placer un ou plusieurs modificateurs d'une lettre immédiatement après le délimiteur final, dans n'importe quel ordre. Par souci de clarté, les modificateurs sont souvent appelés « le modificateur /o » et prononcés « le modificateur slash oh », même si le modificateur final peut être autre chose qu'un slash. (Certaines personnes disent « drapeau » ou « option » pour « modificateur ». C'est bien aussi.)

Certains modificateurs changent le comportement d'un seul opérateur, aussi les décrirons-nous en détail plus loin. D'autres changent la façon dont la regex est interprétée. Les opérateurs m//, s/// et qr//(73) acceptent tous les modificateurs suivants après le délimiteur final :

Modificateur

Signification

/i

Ignore les distinctions de casse des caractères (insensible à la casse).

/s

Fait correspondre . avec le saut de ligne et ignore la variable obsolète $*.

/m

Fait correspondre ^ et $ à côté d'un \n intérieur.

/x

Ignore (la plupart) des blancs et autorise les commentaires dans le motif.

/o

Ne compile le motif qu'une seule fois.

Le modificateur /i indique de faire la correspondance à la fois en majuscules et en minuscules (et en casse de titre en Unicode). Ainsi /perl/i correspondrait avec « SUPERLATIF » ou « Perlimpinpin » (entre autres choses). Le pragma use locale peut aussi avoir une influence sur ce qui est considéré comme équivalent. (Cela peut avoir une mauvaise influence sur les chaînes contenant de l'Unicode.)

Les modificateurs /s et /m n'impliquent rien de coquin. Ils affectent la manière dont Perl traite les correspondances avec des chaînes contenant des sauts de ligne. Ils n'indiquent pas si votre chaîne contient réellement des sauts de ligne ; mais plutôt si Perl doit supposer que votre chaîne contient une seule ligne (/s) ou plusieurs lignes (/m), car certains métacaractères fonctionnent différemment selon qu'ils sont censés se comporter d'une manière orientée ligne ou non.

D'ordinaire le métacaractère « . » correspond à tout caractère sauf un saut de ligne, parce que sa signification traditionnelle est de correspondre aux caractères à l'intérieur d'une ligne. Avec un /s cependant, le métacaractère « . » peut aussi correspondre à des sauts de ligne, car vous avez dit à Perl d'ignorer le fait que la chaîne contient plusieurs sauts de ligne. (Le modificateur /s fait aussi ignorer à Perl la variable obsolète $*, dont nous espérons que vous l'ignoriez aussi.) Le modificateur /m, de son côté, change l'interprétation des métacaractères ^ et $ en leur permettant de correspondre à côté de sauts de lignes à l'intérieur de la chaîne au lieu de considérer seulement les extrémités de la chaîne. Voir les exemples dans la section Positions plus loin dans ce chapitre.

Le modificateur /o contrôle la recompilation des motifs. Sauf si les délimiteurs sont des apostrophes simples, (m'MOTIF', s'MOTIF'REMPLACEMENT', ou qr'MOTIF'), toute variable dans le motif sera interpolée (et pourra provoquer la recompilation du motif) à chaque fois que l'opérateur de motif sera évalué. Si vous voulez qu'un motif ne soit compilé qu'une fois et une seule, servez-vous du modificateur /o. Cela empêche une recompilation coûteuse à l'exécution ; cela peut servir quand la valeur interpolée ne change pas au cours de l'exécution. Cependant, utiliser /o revient à faire la promesse de ne pas modifier les variables dans le motif. Si vous les modifiez, Perl ne s'en rendra même pas compte. Pour avoir un meilleur contrôle de la recompilation des motifs, utilisez l'opérateur de citation de regex qr//. Pour plus de détails, voir la section Interpolation de variables plus loin dans ce chapitre.

Le modificateur /x est l'expressif : il vous permet d'exploiter les blancs et les commentaires explicatifs pour exalter la lisibilité de votre motif, ou même lui permettre d'explorer au-delà des limites des lignes.

Euh, c'est-à-dire que /x modifie la signification des blancs (et du caractère #) : au lieu de les laisser se correspondre à eux-mêmes comme le font les caractères ordinaires, il les transforme en métacaractères qui, étrangement, se comportent comme les blancs (et les caractères de commentaires) devraient le faire. /x permet donc l'utilisation d'espaces, de tabulations et de sauts de lignes pour le formatage, exactement comme le code Perl habituel. Il permet également l'utilisation du caractère #, qui n'est normalement pas un caractère spécial dans un motif, pour introduire un commentaire qui s'étend jusqu'au bout de la ligne en cours à l'intérieur de la chaîne de motif.(74) Si vous voulez détecter un véritable espace (ou le caractère #), alors vous devrez le mettre dans une classe de caractères, le protéger avec un antislash ou bien l'encoder en octal ou en hexadécimal. (Mais les blancs sont normalement détectés avec une séquence \s* ou \s+, donc la situation ne se rencontre pas souvent en pratique.)

À elles toutes, ces fonctionnalités font beaucoup pour rendre les expressions régulières plus proches d'un langage lisible. Dans l'esprit de TMTOWTDI, il existe donc plus d'une manière d'écrire une même expression rationnelle. En fait, il y a plus de deux manières :

 
Sélectionnez
m/\w+:(\s+\w+)\s*\d+/;      # Mot, deux-points, espace, mot, espace, chiffres.

m/\w+: (\s+ \w+) \s* \d+/x; # Mot, deux-points, espace, mot, espace, chiffres.

m{
    \w+:                    # Détecte un mot et un deux-points.
    (                       # (début de groupe)
        \s+                 # Détecte un ou plusieurs blancs.
        \w+                 # Détecte un autre mot.
    )                       # (fin de groupe)
    \s*                     # Détecte zéro ou plusieurs blancs.
    \d+                     # Détecte des chiffres.
}x;

Nous expliquerons ces nouveaux métasymboles plus loin dans ce chapitre. (Cette section était supposée décrire les modificateurs de motifs, mais nous l'avons laissé dériver dans notre excitation au sujet de /x. Bref.) Voici une expression régulière qui trouve les mots doublés dans un paragraphe, emprunté à Perl en action (The Perl Cookbook). Il utilise les modificateurs /x et /i, ainsi que le modificateur /g décrit plus loin.

 
Sélectionnez
# Trouve les doublons dans les paragraphes, si possible malgré les
# enjambements.
# Utilise /x pour les espaces et les commentaires, / pour détecter les
# deux 'tout' dans "Tout tout va bien ?", et /g pour trouver tous les
# doublons.
$/ = "";        # mode "paragrep"
while (<>) {
    while ( m{
                \b           # commence à une limite de mot
                (\w\S+)      # trouve un morceau de "mot"
                (
                    \s+      # séparé par des blancs
                    \1       # et ce même morceau
                ) +          # ad lib
                \b           # jusqu'à une autre limite de mot
            }xig
        )
    {
        print "doublon '$1' au paragraphe $.\n";
    }
}

Quand on l'exécute sur ce chapitre, il affiche des avertissements tels que :

 
Sélectionnez
doublon 'nous' au paragraphe 36

Bien sûr, nous nous sommes aperçus que dans ce cas particulier c'est normal.

5-2-b. L'opérateur m// (match)
 
Sélectionnez
EXPR =~ m/MOTIF/cgimosx
EXPR =~ /MOTIF/cgimosx
EXPR =~ ?MOTIF?cgimosx
m/MOTIF/cgimosx
/MOTIF/cgimosx
?MOTIF?cgimosx

L'opérateur m// recherche dans la chaîne contenue dans le scalaire EXPR le motif MOTIF. Si le délimiteur est / ou ?, le m initial est facultatif. ? et ' ont tous deux une signification particulière en tant que délimiteurs : le premier fait une détection à usage unique, le second supprime l'interpolation de variables et les six séquences de traduction (\U et autres, décrits plus loin).

Si l'évaluation de MOTIF conduit à une chaîne vide, soit parce que vous l'avez spécifié ainsi en utilisant // ou parce qu'une variable interpolée a donné la chaîne vide, la dernière expression régulière exécutée avec succès sans être cachée à l'intérieur d'un bloc inclus (ou à l'intérieur d'un split, grep ou map) est exécutée à la place.

En contexte scalaire, l'opérateur renvoie vrai (1) en cas de succès, et faux ("") sinon. Cette forme se rencontre habituellement en contexte booléen :

 
Sélectionnez
if ($comte =~ m/Sacquet/) { ... } # recherche Sacquet dans la $comté
if ($comte =~ /Sacquet/)  { ... } # recherche Sacquet dans la $comté

if ( m#Sacquet# ) { ... } # cherche ici dans $_
if ( /Sacquet/ )  { ... } # cherche ici dans $

Utilisé en contexte de liste, m// renvoie la liste des sous-chaînes détectées par les parenthèses de capture du motif (c'est-à-dire $1, $2, $3 et ainsi de suite), comme cela est décrit plus loin dans la section Capture et regroupement. Les variables numérotées sont modifiées malgré tout, même si la liste est retournée. Si la recherche réussit en contexte de liste, mais qu'il n'y avait pas de parenthèses de capture (ni de /g), une liste consistant en

(1) est retournée. Comme elle renvoie la liste vide en cas d'échec, cette forme de m// peut aussi être utilisée en contexte booléen, mais seulement quand elle y participe indirectement via une affectation de liste :

 
Sélectionnez
if (($cle,$valeur) = /(\w+): (.*)/) { ... }

Les modificateurs valides pour m// (sous toutes ses formes) sont listés dans le tableau 5-1.

Tableau 5-1. Modificateurs de m//

Modificateur

Signification

/i

Ignore la casse des caractères.

/m

Permet à ^ et $ de fonctionner à côté d'un \n.

/s

Permet au . de détecter des sauts de lignes et ignore la variable obsolète $*.

/x

Ignore (presque tous) les blancs et permet les commentaires à l'intérieur du motif.

/o

Compile le motif une seule fois.

/g

Recherche globale, pour détecter toutes les occurrences.

/cg

Permet de continuer la recherche après un échec dans /g.

Les cinq premiers modificateurs s'appliquent à la regex et ont été décrits précédemment. Les deux derniers modifient le comportement de l'opérateur lui-même. Le modificateur /g indique une détection globale — c'est-à-dire que l'on recherche autant de fois qu'il est possible à l'intérieur de la chaîne. La façon dont cela se comporte dépend du contexte. En contexte de liste, m//g renvoie la liste de toutes les occurrences trouvées. Dans l'exemple suivant, nous trouvons toutes les endroits où quelqu'un a mentionné « perl », « Perl », « PERL », et ainsi de suite :

 
Sélectionnez
if (@perls = $paragraphe =~ /perl/gi) {
    printf "Perl a été mentionné %d fois.\n", scalar @perls;
}

S'il n'y a pas de parenthèses de capture dans le motif /g, alors les correspondances complètes sont renvoyées. S'il y a des parenthèses de capture, seules les chaînes capturées sont retournées. Imaginez une chaîne comme celle-ci :

 
Sélectionnez
$chaine = "password=xyzzy verbose=9 score=0";

Imaginez également que vous souhaitez initialiser un hachage comme ceci :

 
Sélectionnez
%hash = (password => "xyzzy", verbose => 9, score => 0);

Sauf évidemment que vous n'avez pas une liste, vous avez une chaîne. Pour obtenir la liste correspondante, vous pouvez utiliser l'opérateur m//g en contexte de liste pour capturer tous les couples clef/valeur de la chaîne :

 
Sélectionnez
%hash = $chaine =~ /(\w+)=(\w+)/g;

La séquence (\w+) capture un mot alphanumérique. Voir la section Capture et regroupement.

Utilisé en contexte scalaire, le modificateur /g indique une détection progressive, qui reprend une nouvelle recherche sur la même chaîne à la position qui suit celle où la dernière s'est arrêtée. L'assertion \G représente cette position dans la chaîne. Voir la section Positions plus loin dans ce chapitre pour une description de \G. Si vous utilisez le modificateur /c (pour « continuer ») en plus de /g, alors quand le /g ne détecte plus rien, la détection ratée ne réinitialise pas le pointeur de position.

Si le délimiteur est ?, comme dans ?MOTIF?, la recherche fonctionne comme un /MOTIF/ normal, sauf qu'elle ne trouve qu'une seule occurrence entre deux appels à l'opérateur reset. Cela peut être une optimisation utile, quand vous désirez détecter seulement la première occurrence du motif pendant l'exécution du programme, et pas toutes les occurrences. L'opérateur fait la recherche à chaque fois que vous l'appelez, jusqu'à ce qu'il trouve finalement quelque chose, à la suite de quoi il se bloque de lui-même, renvoyant faux jusqu'à ce que vous le réenclenchiez avec reset. Perl se souvient de l'état de la détection pour vous.

L'opérateur ?? est particulièrement utile quand une recherche de motif ordinaire trouverait la dernière occurrence plutôt que la première :

 
Sélectionnez
open DICT, "/usr/dict/words_fr" or die "Impossible d'ouvrir words_fr: $!\n";
while (<DICT>) {
    $premier = $1 if ?(^neur.*)?;
    $dernier = $1 if /(^neur.*)/;
}
print $premier,"\n";    # affiche "neural"
print $dernier,"\n";    # affiche "neurula"

L'opérateur reset ne réinitialisera que les instances de ?? qui ont été compilées dans le même paquetage que l'appel à reset. m?? et ?? sont équivalents.

5-2-c. L'opérateur s/// (substitution)
 
Sélectionnez
LVALUE =~ s/MOTIF/REMPLACEMENT/egimosx
s/MOTIF/REMPLACEMENT/egimosx

Cet opérateur recherche MOTIF dans une chaîne, et s'il le détecte, remplace la chaîne correspondante par le texte de REMPLACEMENT. (Les modificateurs sont décrits plus loin dans cette section.)

 
Sélectionnez
$lotr = $hobbit;             # Copie juste Bilbon le hobbit
$lotr =~ s/Bilbon/Frodon/g;  # et écrit très simplement une suite.

La valeur de retour d'un s/// (en contexte scalaire comme en contexte de liste) est le nombre de fois qu'il a réussi (ce qui peut être plus d'une fois s'il a été utilisé avec le modificateur /g décrit plus tôt). En cas d'échec, il renvoie faux (""), qui est numériquement équivalent à 0.

 
Sélectionnez
if ($lotr =~ s/Bilbon/Frodon/) { print "Suite écrite avec succès." }
$changements = $lotr =~ s/Bilbon/Frodon/g;

La partie de remplacement est traitée comme une chaîne entre apostrophes doubles. Vous pouvez utiliser toutes les variables à portée dynamique décrites plus tôt ($`, $&, $, $1, $2, et ainsi de suite) dans la chaîne de remplacement, ainsi que tous les trucs utilisables avec des apostrophes doubles. Voici un exemple qui trouve toutes les chaînes « revision », « version », ou « release », et les remplace chacune par son équivalent avec une majuscule, à l'aide de la séquence d'échappement \u dans la partie de remplacement :

 
Sélectionnez
s/revision|version|release/\u$&/g; # Utilisez | pour dire "ou"
                                   # dans un motif

Toutes les variables scalaires sont interpolées en contexte d'apostrophes doubles, et pas seulement les étranges que nous venons de voir. Supposons que vous ayez un hachage %Noms qui fasse correspondre aux numéros de version des noms de projet interne ; par exemple $Noms{"3.0"} pourrait avoir le nom de code « Isengard ». Vous pourriez utiliser s/// pour trouver les numéros de version et les remplacer par le nom des projets correspondants :

 
Sélectionnez
s/version ([0-9.]+)/la livraison $Noms{$1}/g;

Dans la chaîne de remplacement, $1 retourne ce que la première (et unique) paire de parenthèses a capturé. (Vous auriez pu aussi utiliser \1 comme vous l'auriez fait dans le motif, mais cet emploi est obsolète dans le remplacement. Dans une chaîne entre apostrophes doubles normale, \1 signifie Contrôle-A.)

Si MOTIF est une chaîne vide, la dernière expression régulière exécutée avec succès est utilisée à la place. MOTIF et REMPLACEMENT sont tous deux sujets à l'interpolation de variables. Cependant, un MOTIF est interpolé à chaque évaluation du s/// en tant que tel, tandis que le REMPLACEMENT n'est évalué qu'à chaque fois qu'une correspondance est trouvée. (Le MOTIF peut correspondre plusieurs fois en une évaluation si vous utilisez le modificateur /g.)

Comme précédemment, les cinq modificateurs du tableau 5-2 modifient le comportement de la regex ; ce sont les mêmes que pour m// et qr//. Les deux derniers affectent l'opérateur de substitution lui-même.

Tableau 5-2. Modificateurs de s///

Modificateur

Signification

/i

Ignore la casse des caractères (lors de la correspondance).

/m

Permet à ^ et $ de fonctionner à côté d'un \n.

/s

Permet au . de détecter des sauts de ligne et ignore la variable obsolète $*.

/x

Ignore (presque tous) les blancs et permet les commentaires à l'intérieur du motif.

/o

Compile le motif une seule fois.

/g

Replacement global, c'est-à-dire de toutes les occurrences.

/e

Évalue la partie droite comme une expression.

Le modificateur /g est utilisé avec s/// pour remplacer chaque occurrence de MOTIF par la valeur REMPLACEMENT, et pas seulement la première rencontrée. Un opérateur s///g agit comme un rechercher-remplacer global, en faisant toutes les modifications en une seule fois. Tout comme un m//g en contexte de liste, sauf que m//g ne change rien. (Et que s///g ne fait pas de détection progressive comme le faisait un m//g en contexte scalaire.)

Le modificateur /e traite le REMPLACEMENT comme s'il s'agissait d'un bout de code Perl plutôt que d'une chaîne interpolée. Le résultat de l'exécution de ce code est utilisé comme chaîne de remplacement. Par exemple, s/([0-9]+)/sprintf("%#x", $1)/ge remplacerait 2581 par 0xb23. Ou supposez que dans notre exemple précédent, vous n'ayez pas été sûr de disposer d'un nom pour toutes les versions et que vous ayez voulu laisser inchangées les versions sans nom. Avec un formatage un peu créatif de /x, vous auriez pu écrire :

 
Sélectionnez
s{
    version
    \s+
    (
        [0-9.]+
    )
}{
    $Noms{$1}
        ? "la livraison $Names{$1}"
        : $&
}xge;

La partie droite de votre s///e (ou dans ce cas, la partie basse) est vérifiée syntaxiquement et compilée en même temps que le reste de votre programme. Toute erreur de syntaxe est détectée à la compilation, et les exceptions dynamiques ne sont pas relevées. Chaque nouveau /e ajouté à la suite du premier (comme /ee, /eee et ainsi de suite) revient à appeler eval CHAINE sur le résultat du code, une fois par /e supplémentaire. Ceci évalue le résultat du code dans l'expression et capture les exceptions dans la variable spéciale $@. Voir la section Motifs programmatiques plus loin dans ce chapitre pour plus de détails.

5-2-c-i. Modifier les chaînes en passant

Parfois vous voulez une nouvelle chaîne, modifiée sans altérer celle sur laquelle elle est basée. Au lieu d'écrire :

 
Sélectionnez
$lotr = $hobbit;
$lotr =~ s/Bilbon/Frodon/g;

vous pouvez combiner ces deux instructions en une seule. À cause de la précédence, des parenthèses sont requises autour de l'affectation, tout comme dans la plupart des combinaisons appliquant =~ à une expression.

 
Sélectionnez
($lotr = $hobbit) =~ s/Bilbon/Frodon/g;

Sans les parenthèses autour de l'affectation, vous n'auriez fait que changer $hobbit et récupérer le nombre de modifications dans $lotr, ce qui nous aurait donné une suite fort peu intéressante.

Vous ne pouvez pas utiliser un opérateur s/// directement sur un tableau. Pour cela, vous aurez besoin d'une boucle. Par une heureuse coïncidence, l'idiome Perl standard pour rechercher et modifier sur chaque élément d'un tableau s'obtient grâce au système d'alias de for/foreach, combiné avec l'utilisation de $_ comme variable par défaut de la boucle :

 
Sélectionnez
for (@chapitres) { s/Bilbon/Frodon/g } # Fait les substitutions chapitre
                                       # par chapitre.
s/Bilbon/Frodon/g for @chapitres;      # Pareil.

Comme avec les variables scalaires, vous pouvez également combiner substitution et affectation si vous désirez conserver une copie de l'original :

 
Sélectionnez
@anciens = ('oiseau bleu', 'bleu roi', 'bleu nuit', 'bleu de travail');
for (@nouveaux = @anciens) { s/bleu/rouge/ }
print "@nouveaux\n"; # affiche : oiseau rouge rouge roi rouge
                     # nuit rouge de travail

La manière idiomatique d'appliquer des substitutions répétées sur une même variable est d'utiliser une boucle à un seul tour. Par exemple, voici comment normaliser les blancs dans une variable :

 
Sélectionnez
for ($chaine) {
    s/^\s+//;       # supprime les blancs au début
    s/\s+$//;       # supprime les blancs à la fin
    s/\s+/ /g;      # minimise les blancs internes
}

ce qui produit exactement le même résultat que :

 
Sélectionnez
$chaine = join(" ", split " ", $chaine);

Vous pouvez également utiliser une telle boucle dans une affectation, comme nous l'avons fait dans le cas du tableau :

 
Sélectionnez
for ($nouveau = $ancien) {
    s/Fred/Homer/g;
    s/Wilma/Marge/g;
    s/Pebbles/Lisa/g;
    s/Dino/Bart/g;
}
5-2-c-ii. Quand une substitution globale n'est pas assez globale

De temps en temps, vous ne pouvez tout simplement pas utiliser un /g pour que tous les changements soient faits, soit parce que les substitutions se produisent de droite à gauche, soit parce que la longueur de $` doit changer entre deux occurrences. En général, vous pouvez faire cela en appelant s/// à répétition. Mais vous voulez tout de même que la boucle s'arrête quand le s/// finit par échouer, ce qui ne laisse rien à faire dans la partie principale de la boucle. Alors nous écrivons un simple 1, qui est une chose plutôt ennuyeuse à faire, mais souvent le mieux que vous pouvez espérer. Voici quelques exemples qui utilisent quelques-unes de ces étranges bestioles que sont les regex :

 
Sélectionnez
# met des points aux bons endroits dans un entier
1 while s/(\d)(\d\d\d)(?!\d)/$1.$2/;

# change les tabulations pour des espacements sur 8 colonnes
1 while s/\t+/' ' x (length($&)*8 - length($`)%8)/e;

# supprime les parenthèses (imbriquées (ou même très imbriquées (comme ça)))
1 while s/\([^()]*\)//g;

# supprime les doublons (et les triplons (et les quadruplons...))
1 while s/\b(\w+) \1\b/$1/gi;

Cette dernière nécessite une boucle, car sinon elle transformerait ceci :

 
Sélectionnez
Paris AU AU AU AU printemps.

en cela :

 
Sélectionnez
Paris AU AU printemps.
5-2-d. L'opérateur tr/// (translittération)
 
Sélectionnez
LVALUE =~ tr/LISTEDERECHERCHE/LISTEDEREMPLACEMENT/cds tr/LISTEDERECHERCHE/LISTEDEREMPLACEMENT/cds

Pour les fans de sed, y/// est fourni comme synonyme de tr///. C'est la raison pour laquelle vous ne pouvez pas plus appeler une fonction y que vous ne pouvez l'appeler q ou m. Hormis cela, y/// est exactement identique à tr/// et nous ne le mentionnerons plus.

Cet opérateur n'aurait pas vraiment de raison d'apparaître dans un chapitre consacré à la détection de motif, puisqu'il n'utilise pas de motif. Cet opérateur balaie une chaîne caractère par caractère, et remplace chaque occurrence d'un caractère de LISTEDERECHERCHE (qui n'est pas une expression régulière) par le caractère correspondant dans LISTEDEREMPLACEMENT (qui n'est pas une chaîne de remplacement). Cependant, il ressemble un peu à m// et à s///, et vous pouvez même utiliser les opérateurs de lien =~ et !~ dessus. C'est pourquoi nous le décrivons ici. (qr// et split sont des opérateurs de recherche de motif, mais on ne peut pas utiliser les opérateurs de lien dessus, c'est pourquoi ils sont ailleurs dans ce livre. Allez comprendre.)

La translittération retourne le nombre de caractères remplacés ou effacés. Si aucune chaîne n'est spécifiée par les opérateurs =~ ou !~, c'est la chaîne $_ qui est modifiée. LISTEDERECHERCHE et LISTEDEREMPLACEMENT peuvent définir des intervalles de caractères successifs à l'aide d'un tiret :

 
Sélectionnez
$message =~ tr/A-Za-z/N-ZA-Mn-za-m/;    # cryptage rot13.

Remarquez qu'un intervalle comme A-Z suppose un ensemble de caractères linéaire comme l'est la table ASCII. Mais chaque ensemble de caractères a son propre point de vue concernant l'ordre de ses caractères, et donc de quels caractères font partie d'un intervalle particulier. Un principe sain est d'utiliser des intervalles qui commencent et finissent sur des caractères de la même casse (a-e, A-E) ou des chiffres (0-4). Tout le reste est suspect. En cas de doute, décrivez l'ensemble qui vous intéresse en entier : ABCDE.

LISTEDERECHERCHE et LISTEDEREMPLACEMENT ne subissent pas d'interpolation de variable comme des chaînes entre apostrophes doubles ; vous pouvez cependant utiliser les séquences avec antislash qui correspondent à des caractères spécifiques, comme \n ou \015.

Le tableau tableau 5-3 donne la liste des modificateurs qui s'appliquent à l'opérateur tr///. Ils sont complètement différents de ceux que vous appliquez à m//, s/// ou qr//, même si certains d'entre eux y ressemblent. Si le modificateur /c est spécifié, le jeu de caractères dans LISTEDERECHERCHE est complémenté ; c'est-à-dire que la liste de recherche effective comprend tous les caractères qui ne se trouvent pas dans LISTEDERECHERCHE. Dans le cas de l'Unicode, cela peut représenter un grand nombre de caractères, mais comme ils sont stockés logiquement et non physiquement, vous n'avez pas à vous inquiéter d'un éventuel manque de mémoire.

Tableau 5-3. Modificateurs de tr///

Modificateur

Signification

/c

Complémente la LISTEDERECHERCHE.

/d

Supprime les caractères trouvés, mais non remplacés.

/s

Concentre les caractères dupliqués remplacés.

Le modificateur /d transforme tr/// en ce qu'on pourrait appeler l'opérateur de « transoblitération » : tout caractère spécifié par LISTEDERECHERCHE, mais qui n'a pas de caractère de remplacement dans LISTEDEREMPLACEMENT est effacé. (C'est un peu plus souple que le comportement de certains tr(1), qui effacent tout ce qu'ils trouvent dans LISTEDERECHERCHE, point.)

Si le modificateur /s est spécifié, les suites de caractères qui sont convertis en un caractère identique sont réduites à un seul exemplaire de ce caractère.

Si le modificateur /d est utilisé, LISTEDEREMPLACEMENT est toujours interprétée comme elle est spécifiée. Sinon, si LISTEDEREMPLACEMENT est plus courte que LISTEDERECHERCHE, le dernier caractère est répliqué jusqu'à ce qu'elle soit assez longue. Si LISTEDEREMPLACEMENT est vide, la LISTEDERECHERCHE est répliquée, ce qui est étonnamment utile si vous voulez juste compter les caractères, et non les modifier. C'est aussi utile pour compacter les séquences de caractères. avec /s.

 
Sélectionnez
tr/aeiou/!/;                  # change toutes les voyelles en !
tr{/\\\r\n\b\f. }{_};         # change les caractères bizarres en soulignés

tr/A-Z/a-z/ for @ARGV;        # normalise en minuscules ASCII

$compte = ($para =~ tr/\n//); # compte les sauts de lignes dans $para
$compte = tr/0-9//;           # compte les chiffres dans $_

$mot =~ tr/a-zA-Z//s;         # illettrisme -> iletrisme

tr/@$%*//d;                   # supprime tous ceux-là
tr#A-Za-z0-9+/##cd;           # supprime les caractères hors base64

# change en passant
($HOTE = $hote) =~ tr/a-z/A-Z/;

$pathname =~ tr/a-zA-Z/_/cs;  # change les caractères ASCII non
                              # alphabétiques en un seul souligné

tr [\200-\377]
   [\000-\177];               # supprime le 8e bit de chaque octet

Si le même caractère apparaît plusieurs fois dans la LISTEDERECHERCHE, seul le premier est utilisé. Ainsi, ceci :

 
Sélectionnez
tr/AAA/XYZ/

changera tout A en X (dans $_). Bien que les variables ne soient pas interpolées par tr///, vous pouvez obtenir le même

effet en utilisant eval EXPR :

 
Sélectionnez
$compte = eval "tr/$ancien/$nouveau/";
die if $@;  # propage l'exception due à un contenu de eval incorrect

Encore un mot : si vous voulez passer votre texte en majuscules ou en minuscules, n'utilisez pas tr///. Utilisez plutôt les séquences \U ou \L dans des chaînes entre apostrophes doubles (l'équivalent des fonctions uc et lc), car elles seront attentives aux informations de locales et à l'Unicode, alors que tr/a-z/A-Z ne le sera pas. De plus, dans les chaînes Unicode, la séquence \u et la fonction correspondante ucfirst comprennent la notion de casse de titre, ce qui pour certaines langues peut être différent de simplement convertir en majuscules.

5-3. Métacaractères et métasymboles

Maintenant que nous avons admiré les jolies cages, nous pouvons de nouveau nous intéresser aux bestioles dans ces cages, les étranges caractères que vous mettez dans les motifs. Maintenant vous avez compris que ces symboles ne sont pas du code Perl normal comme les appels de fonction ou les opérateurs arithmétiques. Les expressions régulières ont leur propre petit langage inclus dans Perl. (Chacun a son jardin et sa jungle secrète.)

Malgré leur puissance et leur expressivité, les motifs de Perl reconnaissent les mêmes 12 caractères traditionnels (les « Douze salopards », s'il en est) qu'on trouve dans de nombreux autres modules d'expressions régulières.

 
Sélectionnez
\|()[ { ^ $ * + ? .

Certains d'entre eux changent les règles, rendant spéciaux les caractères normaux qui les suivent. Nous n'aimons pas appeler les séquences de plus d'un caractère des « caractères », c'est pourquoi nous les appellerons métasymboles (ou même seulement « symboles »). Mais au plus haut niveau, ces douze métacaractères sont tout ce dont vous (et Perl) avez à vous inquiéter. Tout le reste procède de là.

Certains métacaractères simples fonctionnent par eux-mêmes, comme ., ^ et $. Ils n'affectent directement rien de ce qui les entoure. Certains métacaractères fonctionnent comme des opérateurs préfixes, contrôlant ce qui les suit, comme \. D'autres fonctionnent comme des opérateurs postfixes, et contrôlent ce qui les précède immédiatement, comme *, + et ?. Un métacaractère, |, fonctionne comme un opérateur infixe, en se tenant entre les deux opérandes qu'il affecte. Il existe même des métacaractères de parenthésage, qui fonctionnent comme des opérateurs « circonfixes », et régissent ce qu'ils contiennent, comme (...) et [...]. Les parenthèses sont particulièrement importantes, car elles définissent les limites de | à l'intérieur, et celles de *, + et ? à l'extérieur.

Si vous n'apprenez qu'un seul des douze métacaractères, choisissez l'antislash. (Euh... et les parenthèses.) C'est parce que l'antislash invalide les autres. Quand un antislash précède un caractère non alphanumérique dans un motif Perl, il en fait toujours un caractère littéral. Si vous avez besoin de faire une correspondance littérale avec l'un des douze métacaractères, vous n'avez qu'à l'écrire précède d'un antislash. Ainsi, \. est un vrai point, \$ un vrai dollar, \\ un vrai antislash, et ainsi de suite. On appelle cela « protéger » le métacaractère, ou simplement l'« antislasher ». (Bien sûr, vous savez déjà que l'antislash sert à supprimer l'interpolation de variable dans les chaînes entre apostrophes doubles.)

Bien qu'un antislash transforme un métacaractère en caractère littéral, il a l'effet inverse sur un caractère alphanumérique le suivant. Il prend ce qui était normal et le rend spécial. C'est-à-dire qu'à eux deux, ils forment un métasymbole. Une liste alphabétique de ces métasymboles se trouve juste en dessous dans le tableau 5-7.

5-3-a. Tables de métasymboles

Dans les tableaux qui suivent, la colonne Atomique indique « Oui » si le métasymbole correspondant est quantifiable (c'est-à-dire s'il peut correspondre à quelque chose qui a une largeur, plus ou moins). Nous avons également utilisé « ... » pour représenter « quelque chose d'autre ». (Consultez la discussion qui suit pour savoir ce que « ... » signifie, si le commentaire du tableau n'est pas suffisant.)

Le tableau 5-4 liste les métasymboles traditionnels de base. Les quatre premiers sont les métasymboles structurels déjà mentionnés, tandis que les trois suivants sont de simples métacaractères. Le métacaractère . est un exemple d'atome, car il correspond à quelque chose qui a une largeur (la largeur d'un caractère, dans ce cas) ; ^ et $ sont des exemples d'assertions, car ils correspondent à quelque chose de largeur nulle et ne sont évalués que pour savoir s'ils sont vrais ou faux.

Tableau 5-4. Métacaractères usuels de regex

Symbole

Atomique

Signification

\...

Variable

Rend le caractère non alphanumérique suivant littéral, rend le caractère alphanumérique suivant méta (peut-être).

...|...

Non

Alternative (correspond à l'un ou l'autre).

(...)

Oui

Regroupement (traite comme une unité).

[...]

Oui

Classe de caractères (correspond à un caractère d'un ensemble).

^

Non

Vrai au début d'une chaîne (ou après toute nouvelle ligne, éventuellement).

.

Oui

Correspond à un caractère (sauf le saut de ligne, normalement).

$

Non

Vrai en fin de chaîne (ou avant toute nouvelle ligne, éventuellement).

Les quantificateurs, décrits plus loin dans leur propre section, indiquent combien de fois l'atome qui les précède (caractère simple ou regroupement) doit correspondre. Ils sont listés dans le tableau 5-5.

Tableau 5-5. Quantificateurs de regex

Quantificateur

Atomique

Signification

*

Non

Correspond 0 fois ou plus (maximal).

+

Non

Correspond 1 fois ou plus (maximal).

?

Non

Correspond 0 ou 1 fois (maximal).

{COMPTE}

Non

Correspond exactement COMPTE fois.

{MIN,}

No

Correspond au moins MIN fois (maximal).

{MIN, MAX}

No

Correspond au moins MIN fois, mais pas plus de MAX fois (maximal).

*?

Non

Correspond 0 fois ou plus (minimal).

+?

Non

Correspond 1 fois ou plus (minimal).

??

Non

Correspond 0 ou 1 fois (minimal).

{MIN,}?

Non

Correspond au moins MIN fois (minimal).

{MIN, MAX}?

Non

Correspond au moins MIN fois, mais pas plus de MAX fois (minimal).

Un quantificateur minimal essaie de correspondre aussi peu que possible dans l'intervalle autorisé. Un quantificateur maximal essaie de correspondre le plus possible, dans l'intervalle autorisé. Par exemple, .+ est sûr de correspondre à au moins un des caractères de la chaîne, mais correspondra à tous s'il en a l'occasion. Ces occasions seront décrites plus loin dans « Le petit Moteur qui /(ne )?pouvait( pas)?/ ».

Vous remarquerez qu'un quantificateur ne peut jamais être quantifié.

Nous voulions fournir une syntaxe extensible pour pouvoir introduire de nouveaux types de métasymboles. Étant donné que nous n'avions qu'une douzaine de métacaractères à notre disposition, nous avons choisi d'utiliser une séquence de regex anciennement incorrecte pour ces extensions syntaxiques arbitraires. Ces métasymboles sont tous de la forme (?CLE...) ; c'est-à-dire une parenthèse (équilibrée) suivie d'un point d'interrogation, suivie par une CLE et le reste du sous-motif. Le caractère CLE indique de quelle extension de regex il s'agit. Voyez le tableau 5-6 pour en avoir la liste. La plupart d'entre eux se comportent bien structurellement, car ils sont basés sur des parenthèses, mais ont également une signification supplémentaire. De nouveau, seuls les atomes peuvent être quantifiés, car ils représentent quelque chose qui est vraiment là (potentiellement).

Tableau 5-6. Séquences de regex étendues

Extension

Atomique

Signification

(?#...)

Non

Commentaire, ignoré.

(?:...)

Oui

Parenthèses de regroupement seul, sans capture.

(?imsx-imsx)

No

Active/désactive les modificateurs de motif.

(?imsx-imsx:...)

Oui

Parenthèses de regroupement, et modificateurs.

(?=...)

Non

Vrai si l'assertion de pré-vision est un succès.

(?!...)

Non

Vrai si l'assertion de pré-vision est un échec.

(?<=...)

Non

Vrai si l'assertion de rétro-vision est un succès.

(?<!...)

Non

Vrai si l'assertion de rétro-vision est un échec.

(?>>...)

Yes

Correspond au motif sans retour arrière.

(?{...})

Non

Exécute le code Perl inclus.

(??{...})

Oui

Fait correspondre la regex du code Perl inclus.

(?(...)...|...)

Oui

Correspond avec un motif si-alors-sinon.

(?(...)...)

Oui

Correspond avec un motif si-alors.

Enfin, tableau 5-7 donne la liste de tous les métasymboles alphanumériques usuels. (Les symboles qui sont traités durant la passe d'interpolation de variables sont indiqués avec un tiret dans la colonne Atomique, car le Moteur ne les voit jamais.)

Tableau 5-7. Métasymboles alphanumériques de regex

Symbole

Atomique

Signification

\0

Oui

Correspond au caractère nul (ASCII NUL).

\NNN

Oui

Correspond au caractère donné en octal, jusqu'à \377.

\n

Oui

Correspond à la nième chaîne capturée (en décimal).

\a

Oui

Correspond au caractère alarme (BEL).

\A

Non

Vrai au début d'une chaîne.

\b

Oui

Correspond au caractère espace arrière (BS).

\b

Non

Vrai à une limite de mot.

\B

Non

Vrai hors d'une limite de mot.

\cX

Oui

Correspond au caractère de contrôle Contrôle-X (\cZ, \c[ , etc.).

\C

Oui

Correspond à un octet (un char en C), même en utf8 (dangereux).

\d

Oui

Correspond à tout caractère numérique.

\D

Oui

Correspond à tout caractère non numérique.

\e

Oui

Correspond au caractère d'échappement (ASCII ESC, pas l'antislash).

\E

Termine la traduction de casse (\L, \U) ou la citation de métacaractères.

\f

Oui

Correspond au caractère de fin de page (FF).

\G

Non

Vrai à la position de fin de recherche du m//g précédent.

\l

Passe le prochain caractère seulement en minuscules.

\L

Minuscules jusqu'au prochain \E.

\n

Oui

Correspond au caractère de saut de ligne (en général NL, mais CR sur les Mac).

\N{NAME}

Oui

Correspond au caractère nommé (\N{greek:Sigma}).

\p{PROP}

Oui

Correspond à tout caractère ayant la propriété citée.

\P{PROP}

Oui

Correspond à tout caractère n'ayant pas la propriété citée.

\Q

Cite les métacaractères (désactive leur côté méta) jusqu'au prochain \E.

\r

Oui

Correspond au caractère de retour chariot (CR en général, mais NL sur les Mac).

\s

Oui

Correspond à tout caractère blanc.

\S

Oui

Correspond à tout caractère non blanc.

\t

Oui

Correspond au caractère de tabulation (HT).

\u

Passe le prochain caractère seulement en grandes initiales.

\U

Passe en majuscules (et non en grandes initiales) jusqu'au prochain \E.

\w

Oui

Correspond à tout caractère de « mot » (alphanumériques et « _ »)

\W

Oui

Correspond à tout caractère non « mot ».

\x{abcd}

Oui

Correspond au caractère donné en hexadécimal.

\X

Oui

Correspond à une « séquence de caractères Unicode combinés ».

\z

Non

Vrai seulement en fin de chaîne.

\Z

Non

Vrai en fin de chaîne ou avant un éventuel saut de ligne.

Les accolades sont facultatives pour \p et \P si le nom de la propriété fait un seul caractère. Les accolades sont facultatives pour \x si le nombre hexadécimal fait deux chiffres ou moins. Les accolades ne sont jamais facultatives pour \N.

Seuls les métasymboles dont la description est « Correspond à... » peuvent être utilisés dans une classe de caractères. C'est-à-dire que les classes de caractères ne peuvent contenir que des ensembles spécifiques de caractères, et vous ne pouvez donc utiliser à l'intérieur que des métasymboles qui décrivent d'autres ensembles spécifiques de caractères ou qui décrivent des caractères individuels. Bien sûr, ces métasymboles peuvent aussi être utilisés en dehors des classes de caractères, avec les autres métasymboles (qui ne peuvent être utilisés qu'hors classe). Remarquez cependant que \b est en fait deux bestioles complètement différentes : c'est un espace arrière dans une classe de caractères, mais une assertion de limite de mot en dehors.

Il y a un certain chevauchement entre les caractères auxquels un motif peut correspondre et les caractères qu'une chaîne entre apostrophes doubles peut interpoler. Comme les regex subissent deux passes, il est parfois ambigu de savoir quelle passe doit traiter quel caractère. Quand il y a une ambiguïté, la passe d'interpolation de variable défère l'interprétation de ces caractères à l'analyseur d'expressions régulières.

Mais la passe d'interpolation de variable ne peut déférer à l'analyseur de regex que quand elle sait qu'elle est en train d'analyser une regex. Vous pouvez spécifier des expressions régulières comme des chaînes entre apostrophes doubles ordinaires, mais vous devez alors suivre les règles normales des apostrophes doubles. Chacun des métasymboles précédents qui correspond à un caractère existant continuera de marcher, même s'il n'est pas déféré à l'analyseur de regex. Mais vous ne pourrez utiliser aucun des autres métasymboles entre des apostrophes doubles ordinaires (ou dans toute construction analogue comme `...`, qq(...), qx(...) ou les documents ici même équivalents). Si vous voulez que votre chaîne soit analysée comme une expression régulière sans faire de correspondance, vous devriez utiliser l'opérateur qr// (quote regex).

Notez que les séquences d'échappement de casse et de métacitation (\U et compagnie) doivent être traitées durant la passe d'interpolation, car leur rôle est de modifier la façon dont les variables sont interpolées. Si vous supprimez l'interpolation de variable avec des apostrophes simples, vous n'aurez pas non plus les séquences d'échappement. Ni les variables ni les séquences d'échappement (\U, etc.) ne sont étendues dans les chaînes entre apostrophes simples, ni dans les opérateurs m'...' ou qr'...' avec apostrophes simples. Et même si vous faites une interpolation, ces séquences d'échappement sont ignorées si elles apparaissent dans le résultat de l'interpolation de variable, car à ce moment-là il est trop tard pour influencer l'interpolation de variable.

Bien que l'opérateur de translittération ne prenne pas d'expression régulière, tout métasymbole précédemment discuté qui correspond à un caractère spécifique simple marche aussi dans une opération tr///. Les autres ne marchent pas (sauf bien sûr l'antislash qui continue toujours à fonctionner à sa manière bien à lui).

5-4. Caractères spécifiques

Comme mentionné précédemment, tout ce qui n'est pas spécial dans un motif se correspond à lui-même. Cela signifie qu'un /a/ correspond à un « a », un /=/ correspond à un « = », et ainsi de suite. Certains caractères ne sont cependant pas très faciles à taper au clavier ou, même si vous y arrivez ne s'afficheront pas lisiblement ; les caractères de contrôle sont connus pour cela. Dans une expression régulière, Perl reconnaît les alias d'apostrophes doubles suivants :

Échappement

Signification

\0

Caractère nul (ASCII NUL)

\a

Alarme (BEL)

\e

Échappement (ESC)

\f

Fin de page (FF)

\n

Saut de ligne (NL, CR sur Mac)

\r

Retour chariot (CR, NL sur Mac)

\t

Tabulation (HT)

Tout comme dans les chaînes entre apostrophes doubles, Perl reconnaît également les quatre métasymboles suivants dans les motifs :

\cX

  • Un caractère de contrôle nommé, comme \cC pour Contrôle-C, \cZ pour Contrôle-Z, \c[ pour ESC, et \c? pour DEL.

\NNN

  • Un caractère spécifié en utilisant son code octal à deux ou trois chiffres. Le 0 initial est facultatif, sauf pour les valeurs inférieures à 010 (8 en décimal), car (contrairement aux chaînes entre apostrophes doubles) les versions à un seul chiffre sont toujours considérées comme des références arrière à des chaînes capturées dans un motif. Les versions à plusieurs chiffres sont interprétées comme la nième référence arrière si vous avez capturé au moins n sous-chaînes plus tôt dans le motif (avec n considéré comme un nombre décimal). Sinon, elle est interprétée comme un caractère octal.

\x{HEXLONG}\xHEX

  • Un caractère spécifié par son numéro à un ou deux chiffres hexadécimaux ([0-9afA-F]), comme \x1B. La forme à un seul chiffre est utilisable quand le caractère suivant n'est pas un chiffre hexadécimal. Si vous utilisez des accolades, vous pouvez mettre autant de chiffres que vous voulez, ce qui peut conduire à un caractère unicode. Par exemple, \x{262f} correspond à un YIN YANG Unicode.

\N{NOM}

  • Un caractère nommé, comme \N{GREEK SMALL LETTER EPSILON}, \N{greek:epsilon}, ou \N{epsilon}. Ceci requiert l'utilisation du pragma use charnames décrit au chapitre 31, Modules de pragmas, et qui détermine aussi lequel de ces noms vous pouvez utiliser (":long", ":full", ":short" qui correspondent respectivement aux trois styles que nous venons de citer).
  • Vous trouverez une liste de tous les noms de caractères Unicode dans vos documents de standard Unicode préférés ou dans le fichier PATH_TO_PERLLIB/unicode/Names.txt.
5-4-a. Métasymboles jokers

Trois métasymboles spéciaux sont utilisés comme des jokers génériques, chacun d'entre eux correspondant à « tout » caractère (pour certaines valeurs de « tout »). Ce sont le point ("."), \C et \X. Aucun d'entre eux ne peut être utilisé dans une classe de caractères. Vous ne pouvez pas y utiliser le point, car il correspondrait à (presque) tout caractère existant. C'est donc déjà une sorte de classe de caractères universelle par lui-même. Si vous voulez tout inclure ou tout exclure, il n'y a pas grand intérêt à utiliser une classe de caractères. Les jokers spéciaux \C et \X ont une signification structurelle spécifique qui ne s'accorde pas bien avec l'idée de choisir un seul caractère Unicode, ce qui est le niveau auquel fonctionne une classe de caractères.

Le métacaractère point correspond à tout caractère autre que le saut de ligne. (Et avec le modificateur /s, il lui correspond aussi.) Comme chacun des autres caractères spéciaux dans un motif, pour correspondre à un point littéral, vous devez le protéger avec un antislash. Par exemple, ceci vérifie si un nom de fichier se termine par un point suivi d'une extension à un caractère :

 
Sélectionnez
if ($chemin =~ /\.(.)\z/s) {
    print "Se termine par $1\n";
}

Le premier point, celui qui est protégé, est un caractère littéral, tandis que le second dit « correspond à tout caractère ». Le \z demande de ne correspondre qu'à la fin de la chaîne, et le modificateur /s permet au point de correspondre également à un saut de ligne. (Certes, utiliser un saut de ligne comme extension n'est Pas Très Gentil, mais cela ne veut pas dire que cela ne peut pas se produire.)

Le métacaractère point est le plus souvent utilisé avec un quantificateur. Un .* correspond à un nombre maximal de caractères, tandis qu'un .*? correspond à un nombre minimal de caractères. Mais il est aussi utilisé parfois sans quantificateur pour sa propre largeur : /(..):(..):(..)/ correspond à trois champs séparés par des deux-points, chacun faisant deux caractères de large.

Si vous utilisez un point dans un motif compilé sous le pragma à portée lexicale use utf8, alors il correspondra à tout caractère Unicode. (Vous n'êtes pas supposé avoir besoin de use utf8 pour cela, mais il y a toujours des accidents. Le pragma peut ne plus être utile d'ici à ce que vous lisiez ces lignes.)

 
Sélectionnez
use utf8;
use charnames qw/:full/;
$BWV[887] = "G\N{MUSIC SHARP SIGN} minor"; # partition anglaise
($note, $noire, $mode) = $BWV[887] =~ /^([A-G])(.)\s+(\S+)/;
print "C'est en dièse !\n" if $n    eq chr(9839);

Le métasymbole \X correspond à un caractère Unicode dans un sens plus étendu. Il correspond en fait à une chaîne d'un ou plusieurs caractères Unicode connue sous le nom de « séquence de caractères combinés ». Une telle séquence consiste en un caractère de base suivi par l'un des caractères de « marquage » (signe diacritique comme une cédille ou un tréma) qui se combine avec le caractère de base pour former une unité logique. \X est exactement équivalent à (?:\PM\pM*). Cela permet de correspondre à un seul caractère logique, même si celui est en fait constitué de plusieurs caractères distincts. La longueur de la correspondance de /\X/ peut excéder un caractère si celui-ci a trouvé des caractères combinés. (Et nous parlons ici de la longueur en caractères, qui n'a pas grand-chose à voir avec la longueur en octets.)

Si vous utilisez Unicode et que vous voulez vraiment travailler sur un seul octet plutôt que sur un seul caractère, vous pouvez utiliser le métasymbole \C. Ceci correspondra toujours à un seul octet (spécifiquement, à un seul élément de type char de C), même si cela vous désynchronise de votre flux de caractères Unicode. Référez-vous aux avertissements relatifs à cela au chapitre 15.

5-5. Classes de caractères

Dans une recherche de motif, vous pouvez établir une correspondance avec tout caractère qui a — ou n'a pas — une propriété particulière. Il existe quatre manières de spécifier les classes de caractères. Vous pouvez spécifier une classe de caractères de la manière traditionnelle en utilisant des crochets et en énumérant les caractères possibles, ou en utilisant l'un des trois raccourcis mnémotechniques : les classes Perl habituelles, les nouvelles propriétés Unicode de Perl ou les classes POSIX standard. Chacun de ces raccourcis correspond à un seul caractère de son ensemble. Quantifiez-les si vous voulez faire une correspondance avec un plus grand nombre d'entre eux, comme \d+ pour correspondre à plusieurs chiffres. (Une erreur facile à faire est de croire que \w correspond à un mot. Utilisez \w+ pour trouver un mot.)

5-5-a. Classes de caractères personnalisées

Une liste de caractères énumérée entre crochets est appelée une classe de caractère et correspond à tout caractère dans la liste. Par exemple, [aeiouy] correspond à une voyelle (non accentuée). Pour correspondre à un crochet fermant, antislashez-le ou bien placez-le en premier dans la liste.

Des intervalles de caractères peuvent être indiqués en utilisant un tiret et la notation a-

z. Plusieurs intervalles peuvent être combinés ; par exemple [0-9a-fA-F] correspond à un « chiffre » hexadécimal. Vous pouvez utiliser un antislash pour protéger un tiret qui serait sinon interprété comme un délimiteur d'intervalle, ou simplement le placer en début de liste (une pratique probablement moins lisible, mais plus traditionnelle).

Un chapeau (ou accent circonflexe, ou flèche vers le haut) au début de la classe de caractère l'inverse, la faisant correspondre à tout caractère ne se trouvant pas dans la liste. (Pour correspondre à un chapeau, ou bien ne le mettez pas au début, ou mieux, protégez-le avec un antislash.) Par exemple, [^aeiouy] correspond à tout caractère qui n'est pas une voyelle (non accentuée). Cependant, soyez prudent avec la négation de classe, car l'univers des caractères est en expansion. Par exemple, cette classe de caractère correspond aux consonnes — et aussi aux espaces, aux sauts de ligne, et à tout (y compris les voyelles) en cyrillique, en grec ou tout autre alphabet, sans compter tous les idéogrammes chinois, japonais et coréens. Et un jour aussi le cirth, le tengwar et le klingon. (Mais déjà le linéaire B et l'étrusque.) Il vaut donc peut-être mieux spécifier vos consonnes explicitement, comme [bcdfghjklmnpqrstvwxyz], ou [b-df-hj-np-tv-z] pour faire plus court. (Cela résout aussi le problème du « y » qui devrait être à deux endroits à la fois, ce qu'interdit l'utilisation de l'ensemble complémentaire.)

Les métasymboles caractères normaux, comme \n, \t, \cX, \NNN et \N{NOM} sont utilisables dans une classe de caractères (voir la section Caractères spécifiques). De plus, vous pouvez utiliser \b dans une classe de caractères pour signifier un espace arrière, exactement comme dans une chaîne entre apostrophes doubles. Normalement, dans une recherche de motif, il représente une limite de mot. Mais les assertions de largeur nulle n'ont aucun sens dans une classe de caractères, c'est pourquoi \b retrouve la signification qu'il a dans les chaînes. Vous pouvez également utiliser les classes de caractères prédéfinies qui sont décrites plus loin dans ce chapitre (classique, Unicode ou POSIX), mais n'essayez pas de les utiliser comme point initial ou final d'un intervalle — cela n'ayant pas de sens, le « -» sera interprété comme un caractère littéral.

Tous les autres métasymboles perdent leur signification spéciale quand ils sont entre crochets. En particulier, vous ne pouvez utiliser aucun des trois jokers génériques : « . », \X ou \C. Le premier surprend souvent, mais cela n'a pas grand sens d'utiliser la classe de caractère universelle à l'intérieur d'une classe plus restrictive, et de plus vous avez souvent besoin de faire une correspondance avec un point littéral dans une classe de caractère — quand vous travaillez sur des noms de fichier, par exemple. Cela n'a pas grand sens non plus de spécifier des quantificateurs, des assertions ou des alternatives dans une classe de caractères, puisque les caractères sont interprétés individuellement. Par exemple, [fee|fie|foe|foo] a exactement la même signification que [feio|].

5-5-b. Raccourcis de classes de caractères classiques Perl

Depuis l'origine, Perl a fourni un certain nombre de raccourcis pour les classes de caractères. La liste est fournie dans le tableau 5-8. Tous sont des métasymboles alphabétiques antislashés, et dans chaque cas, la version en majuscule est la négation de la version en minuscules. Leur signification est n'est pas aussi rigide que vous pourriez le croire : leur sens peut être influencé par les définitions de locales. Même si vous n'utilisez pas les locales, leur signification peut changer à chaque fois qu'un nouveau standard Unicode sortira, ajoutant de nouveaux scripts avec de nouveaux chiffres et de nouvelles lettres. (Pour conserver l'ancienne signification par octets, vous pouvez toujours utiliser use bytes. Voir Propriétés Unicode un peu plus loin dans ce chapitre pour des explications sur les correspondances utf8. Dans tous les cas, les correspondances utf8 sont un surensemble des correspondances sur les octets.) (Oui, nous savons que la plupart des mots ne comportent ni chiffres ni soulignés, mais parfois des accents ; \w sert à trouver des « mots » au sens des tokens dans un langage de programmation usuel. Ou en Perl.)

Tableau 5-8. classe de caractères classiques

Symbole

Signification

En octets

En utf8

\d

Chiffre

[0-9]

\p{IsDigit}

\D

Non chiffre

[^0-9]

\P{IsDigit}

\s

Blanc

[ \t\n\r\f]

\p{IsSpace}

\S

Non blanc

[^ \t\n\r\f]

\P{IsSpace}

\w

Caractère de mot

[a-zA-Z0-9_]

\p{IsWord}

\W

Non-(caractère mot)

[^a-zA-Z0-9_]

\P{IsWord}

Ces métacaractères peuvent être utilisés à l'extérieur ou à l'intérieur de crochets, c'est-à-dire soit par eux-mêmes ou comme élément d'une classe de caractères :

 
Sélectionnez
if ($var =~ /\D/)       { warn "contient des non-chiffres" }
if ($var =~ /[^\w\s.]/) { warn "contient des non-(mot, espace, point)" }
5-5-c. Propriétés Unicode

Les propriétés Unicode sont disponibles en utilisant \p{PROP} et son ensemble complémentaire \P{PROP}. Pour les rares propriétés qui ont un nom en un seul caractère, les accolades sont facultatives, comme dans \pN pour indiquer un caractère numérique (pas nécessairement décimal — les chiffres romains sont aussi des caractères numériques). Ces classes de propriétés peuvent être utilisées par elles-mêmes ou combinées dans une classe de caractères :

 
Sélectionnez
if ($var =~ /^\p{IsAlpha}+$/)      { print "tout alphabétique" }
if ($var =~ s/[\p{Zl}\p{Zp}]/\n/g) { print "saut de lignes" }

Certaines propriétés sont directement définies dans le standard Unicode, et d'autres sont des composées définies par Perl, en s'appuyant sur les propriétés standard. Zl et Zp sont des propriétés Unicode standard qui représentent les séparateurs de lignes et de paragraphes, tandis qu'IsAlpha est définie par Perl comme une classe regroupant les propriétés standard Ll, Lu, Lt et Lo (c'est-à-dire les lettres qui sont en majuscules, en minuscules, en casse de titre ou autre). À partir de la version 5.6.0 de Perl, vous avez besoin de use utf8 pour que ces propriétés fonctionnent. Cette restriction sera assouplie dans le futur.

Il y a un grand nombre de propriétés. Nous allons lister celles que nous connaissons, mais la liste sera forcément incomplète. De nouvelles propriétés sont susceptibles d'apparaître dans les nouvelles versions d'Unicode, et vous pouvez même définir vos propres propriétés. Nous verrons cela plus loin.

Le Consortium Unicode produit les informations que l'on retrouve dans les nombreux fichiers que Perl utilise dans son implémentation d'Unicode. Pour plus d'informations sur ces fichiers, voyez le chapitre 15. Vous pouvez lire un bon résumé de ce qu'est Uni-code dans le document PATH_TO_PERLLIB/unicode/Unicode3.html PATH_TO_PERLLIB est ce qui est affiché par :

 
Sélectionnez
perl -MConfig -le 'print $Config{privlib}'

La plupart des propriétés Unicode sont de la forme \p{IsPROP}. Le Is est facultatif, mais vous pouvez le laisser pour des raisons de lisibilité.

5-5-c-i. Propriétés Unicode de Perl

Le tableau 5-9 donne la liste des propriétés composites de Perl. Elles sont définies de façon à être raisonnablement proches des définitions POSIX standard pour les classes de caractères.

Tableau 5-9. Propriétés Unicode composites

Propriété

Équivalent

IsASCII

[\x00-\x7f]

IsAlnum

[\p{IsLl}\p{IsLu}\p{IsLt}\p{IsLo}\p{IsNd}]

IsAlpha

[\p{IsLl}\p{IsLu}\p{IsLt}\p{IsLo}]

IsCntrl

\p{IsC}

IsDigit

\p{Nd}

IsGraph

[^\pC\p{IsSpace}]

IsLower

\p{IsLl}

IsPrint

\P{IsC}

IsPunct

\p{IsP}

IsSpace

[\t\n\f\r\p{IsZ}]

IsUpper

[\p{IsLu}\p{IsLt}]

IsWord

[_\p{IsLl}\p{IsLu}\p{IsLt}\p{IsLo}\p{IsNd}]

IsXDigit

[0-9a-fA-F]

Perl fournit également les composites suivants pour chacune des principales catégories des propriétés Unicode standard (voir la section suivante) :

Propriété

Signification

Normative

IsC

Codes de contrôle et équivalents

Oui

IsL

Lettres

Partiellement

IsM

Marqueurs

Oui

IsN

Nombres

Oui

IsP

Ponctuation

Non

IsS

Symboles

Non

IsZ

Séparateurs (Zéparateurs?)

Oui

5-5-c-ii. Propriétés Unicode standard

Le tableau 5-10 donne la liste des propriétés Unicode standard les plus basiques, qui dérivent de la catégorie de chaque caractère. Aucun caractère n'est membre de plus d'une catégorie. Certaines propriétés sont normatives ; d'autres sont simplement informatives. Reportez-vous au standard Unicode pour savoir combien l'information normative est normative, et combien l'information informative ne l'est pas.

Tableau 5-10. Propriétés Unicode standard

Propriété

Signification

Normative

IsCc

Autre, contrôle

Oui

IsCf

Autre, format

Oui

IsCn

Autre, non affecté

Oui

IsCo

Autre, usage privé

Oui

IsCs

Autre, substitut

Oui

IsLl

Lettre, minuscule

Oui

IsLm

Lettre, modificateur

Non

IsLo

Lettre, autre

Non

IsLt

Lettre, casse de titre

Oui

IsLu

Lettre, majuscule

Oui

IsMc

Marque, combinante

Oui

IsMe

Marque, entourant

Oui

IsMn

Marque, non-espacement

Oui

IsNd

Nombre, chiffre décimal

Oui

IsNl

Nombre, lettre

Oui

IsNo

Nombre, autre

Oui

IsPc

Ponctuation, connecteur

Non

IsPd

Ponctuation, tiret

Non

IsPe

Ponctuation, fermeture

Non

IsPf

Ponctuation, guillemet fermant

Non

IsPi

Ponctuation, guillemet ouvrant

Non

IsPo

Ponctuation, autre

Non

IsPs

Ponctuation, ouvert

Non

IsSc

Symbole, monnaie

Non

IsSk

Symbole, modificateur

Non

IsSm

Symbole, math

Non

IsSo

Symbole, autre

Non

IsZl

Séparateur, de ligne

Oui

IsZp

Séparateur, de paragraphe

Oui

IsZs

Séparateur, espace

Oui

Un autre ensemble de propriétés utiles est lié au fait qu'un caractère peut être décomposé (de façon canonique ou compatible) en d'autres caractères plus simples. La décomposition canonique ne perd aucune information de formatage. La décomposition compatible peut perdre des informations de formatage, comme le fait de savoir si le caractère est en exposant.

Propriété

Information perdue

IsDecoCanoni

Rien

IsDecoCompati

Quelque chose (parmi les suivants)

IsDCcirclei

Cercle autour du caractère

IsDCfinali

Préférence de position finale (Arabe)

IsDCfonti

Préférence de variante de fonte

IsDCfractioni

Caractéristique de fraction ordinaire

IsDCinitiali

Préférence de position initiale (Arabe)

IsDCisolatedi

Préférence de position isolée (Arabe)

IsDCmediali

Préférence de position médiane (Arabe)

IsDCnarrowi

Caractéristique d'étroitesse

IsDCnoBreaki

Préférence de non-coupure sur un espace ou un tiret

IsDCsmalli

Caractéristique de petite taille

IsDCsquarei

Carré autour d'un caractère CJK

IsDCsubi

Position d'indice

IsDCsuperi

Position d'exposant

IsDCverticali

Rotation (d'horizontal à vertical)

IsDCwidei

Caractéristique de largeur

IsDCcompati

Identité (variable)

Voici quelques propriétés qui intéressent les personnes faisant du rendu bidirectionnel :

Propriété

Signification

IsBidiL

Gauche à droite (Arabe, Hébreu)

IsBidiLRE

Inclusion de gauche à droite

IsBidiLRO

Force de gauche à droite

IsBidiR

Droite à gauche

IsBidiAL

Droite à gauche arabe

IsBidiRLE

Inclusion de droite à gauche

IsBidiRLO

Force de droite à gauche

IsBidiPDF

Dépile le format directionnel

IsBidiEN

Nombre européen

IsBidiES

Séparateur de nombre européen

IsBidiET

Terminateur de nombre européen

IsBidiAN

Nombre arabe

IsBidiCS

Séparateur de nombres communs

IsBidiNSM

Marque de non-espacement

IsBidiBN

Limite neutre

IsBidiB

Séparateur de paragraphe

IsBidiS

Séparateur de segment

IsBidiWS

Blanc

IsBidiON

Autres neutres

IsMirrored

Image miroir quand utilisé de droite à gauche

Les propriétés suivantes classent différents syllabaires selon les sonorités des voyelles :

IsSylA

IsSylE

IsSylO

IsSylWAA

IsSylWII

IsSylAA

IsSylEE

IsSylOO

IsSylWC

IsSylWO

IsSylAAI

IsSylI

IsSylU

IsSylWE

IsSylWOO

IsSylAI

IsSylII

IsSylV

IsSylWEE

IsSylWU

IsSylC

IsSylN

IsSylWA

IsSylWI

IsSylWV

Par exemple, \p{IsSylA} correspondrait à \N{KATAKANA LETTER KA}, mais pas à \N{KATAKANA LETTER KU}.

Maintenant que nous vous avons parlé de toutes ces propriétés Unicode 3.0, nous devons tout de même lister quelques-unes des plus ésotériques qui ne sont pas implémentées dans Perl 5.6.0, car cette implémentation était basée en partie sur Unicode 2.0, et que des choses comme l'algorithme bidirectionnel étaient encore sur le métier. Cependant, comme d'ici à ce que vous lisiez ceci, les propriétés manquantes pourraient bien avoir été implémentées, nous les avons listées quand même.

5-5-c-iii. Propriétés de bloc Unicode

Certaines propriétés Unicode sont de la forme \p{InSCRIPT}. (Remarquez la distinction entre Is et In.) Les propriétés In servent à tester l'appartenance à des blocs d'un SCRIPT unique. Si vous avez un caractère et que vous vous demandez s'il est écrit en écriture hébraïque, vous pouvez le tester avec :

 
Sélectionnez
print "Pour moi c'est de l'Hébreu!\n" if chr(1488) =~ /\p{InHebrew}/;

Cela fonctionne en vérifiant si un caractère est « dans » l'intervalle valide de ce type d'écriture. Cela peut être inversé avec \P{InSCRIPT} pour savoir si quelque chose n'est pas dans un bloc de script particulier, comme \P{InDingbats} pour savoir si une chaîne contient un caractère non dingbat. Dans les propriétés de bloc, on trouve les éléments suivants :

InArabic

InCyrillic

InHangulJamo

InMalayalam

InSyriac

InArmenian

InDevanagari

InHebrew

InMongolian

InTamil

InArrows

InDingbats

InHiragana

InMyanmar

InTelugu

InBasicLatin

InEthiopic

InKanbun

InOgham

InThaana

InBengali

InGeorgian

InKannada

InOriya

InThai

InBopomofo

InGreek

InKatakana

InRunic

InTibetan

InBoxDrawing

InGujarati

InKhmer

InSinhala

InYiRadicals

InCherokee

InGurmukhi

InLao

InSpecials

InYiSyllables

Sans oublier les impressionnants :

InAlphabeticPresentationForms

InHalfwidthandFullwidthForms

InArabicPresentationForms-A

InHangulCompatibilityJamo

InArabicPresentationForms-B

InHangulSyllables

InBlockElements

InHighPrivateUseSurrogates

InBopomofoExtended

InHighSurrogates

InBraillePatterns

InIdeographicDescriptionCharacters

InCJKCompatibility

InIPAExtensions

InCJKCompatibilityForms

InKangxiRadicals

InCJKCompatibilityIdeographs

InLatin-1Supplement

InCJKRadicalsSupplement

InLatinExtended-A

InCJKSymbolsandPunctuation

InLatinExtended-B

InCJKUnifiedIdeographs

InLatinExtendedAdditional

InCJKUnifiedIdeographsExtensionA

InLetterlikeSymbols

InCombiningDiacriticalMarks

InLowSurrogates

InCombiningHalfMarks

InMathematicalOperators

InCombiningMarksforSymbols

InMiscellaneousSymbols

InControlPictures

InMiscellaneousTechnical

InCurrencySymbols

InNumberForms

InEnclosedAlphanumerics

InOpticalCharacterRecognition

InEnclosedCJKLettersandMonths

InPrivateUse

InGeneralPunctuation

InSuperscriptsandSubscripts

InGeometricShapes

InSmallFormVariants

InGreekExtended

InSpacingModifierLetters

Et le mot le plus long :

  • InUnifiedCanadianAboriginalSyllabics

Pas mieux.

Consultez PATH_TO_PERLLIB/unicode/In/*.pl pour obtenir une liste à jour de toutes ces propriétés de blocs de caractères. Remarquez que les propriétés In ne font que vérifier si le caractère est dans l'intervalle alloué pour ce script. Rien ne vous garantit que tous les caractères dans cet intervalle sont définis ; vous devrez également tester l'une des propriétés Is discutées précédemment pour vérifier si le caractère est défini. Rien ne vous garantit non plus qu'une langue particulière n'utilise pas des caractères en dehors de l'intervalle qui lui est affecté. En particulier, de nombreuses langues européennes mélangent les caractères latins étendus avec des caractères Latin-1.

Mais bon, si vous avez besoin d'une propriété particulière qui n'est pas fournie, ce n'est pas un gros problème. Continuez à lire.

5-5-d. Définir vos propres propriétés de caractères

Pour définir vos propres propriétés, vous devrez écrire un sous-programme portant le nom de la propriété que vous voulez (voir chapitre 6, Sous-programmes). Le sous-programme doit être défini dans le paquetage qui a besoin de la propriété, ce qui signifie que si vous désirez l'utiliser dans plusieurs paquetages, vous devrez soit l'importer d'un autre module (voir chapitre 11, Modules), ou l'hériter comme méthode de classe depuis le paquetage dans lequel il est défini (voir chapitre 12, Objets).

Une fois que cela est fait, le sous-programme doit renvoyer des données dans le même format que les fichiers du répertoire PATH_TO_PERLLIB/unicode/Is. C'est-à-dire que vous devez juste retourner une liste de caractères ou des intervalles de caractères en hexadécimal, un par ligne. S'il y a un intervalle, les deux nombres sont séparés par une tabulation. Supposons que vous vouliez créer une propriété qui serait vraie si votre caractère est dans l'intervalle de l'un des deux syllabaires japonais, nommés hiragana et katakana. (Ensemble on les appelle simplement kana.) Vous pouvez juste mettre les deux intervalles comme suit :

 
Sélectionnez
sub InKana {
    return <<'END';
3040 309F
30A0 30FF
END
}

Vous pourriez également le définir à l'aide de noms de propriétés existantes :

 
Sélectionnez
sub InKana {
    return <<'END';
+utf8::InHiragana
+utf8::InKatakana
END
}

Vous pouvez aussi faire une soustraction en utilisant un préfixe « -». Supposons que vous ne vouliez que les véritables caractères, et pas seulement les blocs de caractères. Vous pouvez retirer tous les caractères indéfinis comme ceci :

 
Sélectionnez
sub IsKana {
    return <<'END';
+utf8::InHiragana
+utf8::InKatakana
-utf8::IsCn
END
}

Vous pouvez aussi démarrer avec le complément d'un ensemble de caractères en utilisant le préfixe « ! »:

 
Sélectionnez
sub IsNotKana {
    return <<'END';
!utf8::InHiragana
-utf8::InKatakana
+utf8::IsCn
END
}

Perl lui-même utilise les mêmes trucs pour définir la signification de ses propres classes de caractères « classiques » (comme \w) quand vous les incluez dans vos classes de caractères personnalisées (comme [-.\w\s]). Vous pourriez penser que plus les règles deviennent compliquées, plus lentement cela tournera ; mais en fait, une fois que Perl a calculé le motif de bits pour le bloc de 64 bits de votre propriété, il le mémorise pour n'avoir jamais à le recalculer. (Cela est fait dans des blocs de 64 bits pour ne même pas avoir à décoder votre utf8 pour faire les recherches.) Ainsi, toutes les classes de caractères, internes ou personnalisées, tournent toutes à peu près à la même vitesse (rapide) une fois mises en route.

5-5-e. Classes de caractères de style POSIX

Contrairement aux autres raccourcis de classes de caractères, la notation de style POSIX, [:CLASSE:], n'est utilisable que pour la construction d'autres classes de caractères, c'est-à-dire à l'intérieur d'une autre paire de crochets. Par exemple, /[.,[:alpha:][:digit:]]/ va chercher un caractère qui est soit un point (littéral, car dans une classe de caractères), une virgule, ou un caractère alphabétique ou un nombre.

Les classes POSIX disponibles à partir de la version 5.6 de Perl sont listées dans le tableau 5-11.

Tableau 5-11. Classes de caractères POSIX

Classe

Signification

alnum

Tout alphanumérique, c'est-à-dire un alpha ou un digit.

alpha

Toute lettre. (Cela fait beaucoup plus de lettres que vous pensez, sauf si vous pensez en Unicode, auquel cas cela fait quand même beaucoup.)

ascii

Tout caractère avec une valeur numérique comprise entre 0 et 127.

cntrl

Tout caractère de contrôle. En général ce sont des caractères qui ne produisent pas d'affichage par eux-mêmes, mais contrôlent le terminal d'une façon ou d'une autre. Par exemple, les caractères de nouvelle ligne, de fin de page ou d'espace arrière sont des caractères de contrôle. Les caractères

ayant

une valeur numérique inférieure à 32 sont le plus souvent considérés comme des caractères de contrôle.

digit

Un caractère représentant un chiffre décimal, comme ceux entre 0 et 9. (Cela comprend d'autres caractères en Unicode.) Équivalent à \d.

graph

Tout caractère alphanumérique ou de ponctuation.

lower

Une lettre minuscule.

print

Tout caractère alphanumérique, de ponctuation ou d'espacement.

punct

Tout caractère de ponctuation.

space

Tout caractère d'espacement. Cela inclut tabulation, nouvelle ligne, saut de page et retour chariot (et encore beaucoup plus en Unicode). Équivalent à \s.

upper

Tout caractère en majuscule (ou casse de titre).

word

Tout caractère d'identificateur, soit un alnum ou un souligné.

xdigit

Tout chiffre hexadécimal. Bien que cela semble un peu bête ([0-9a-fA-F] marche très bien), c'est inclus pour être complet.

Vous pouvez inverser la classe de caractères POSIX en préfixant le nom de la classe par un ^ après le [:. (C'est une extension apportée par Perl.) Par exemple :

POSIX

Classique

[:^digit:]

\D

[:^space:]

\S

[:^word:]

\W

Si le pragma use utf8 n'est pas demandé, mais que le pragma use locale l'est, la classe correspond directement avec les fonctions équivalentes de l'interface isalpha(3) de la librairie C (excepté word, qui est une extension Perl correspondant à \w).

Si le pragma utf8 est utilisé, les classes de caractères POSIX sont exactement équivalentes aux propriétés Is listées dans le tableau 5-9. Par exemple, [:lower:] et \p{Lower} sont équivalentes, sauf que les classes POSIX ne peuvent être utilisées que dans les classes de caractères construites, tandis que les propriétés Unicode ne sont pas soumises à de telles restrictions et peuvent être utilisées dans des motifs partout où les raccourcis Perl comme \s et \w peuvent l'être.

Les crochets font partie de la construction de style POSIX [::] et pas de la classe de caractères. Ce qui nous amène à écrire des motifs tels que /^[[:lower:][:digit:]]+$/ pour correspondre à une chaîne consistant entièrement de lettres minuscules ou de chiffres (avec un saut de ligne final facultatif). En particulier, ceci ne fonctionne pas :

 
Sélectionnez
42 =~ /^[:digit:]+$/  # FAUX

Cela est dû au fait qu'on n'est pas dans une classe de caractères. Ou plutôt, c'est une classe de caractères, celle qui représente les caractères « : », « i », « t », « g » et « d ». Perl se moque du fait que vous ayez utilisé deux « : » (et deux « i »).

Voici ce dont vous avez en fait besoin :

 
Sélectionnez
42 =~ /^[[:digit:]]+$/

Les classes de caractères POSIX [.cc.] et [=cc=] sont reconnues, mais produisent une erreur indiquant qu'elles ne sont pas utilisables. L'utilisation de n'importe quelle classe de caractères POSIX dans une version de Perl plus ancienne ne marchera pas, et de façon probablement silencieuse. Si vous comptez utiliser les classes de caractères POSIX, il est préférable d'imposer l'utilisation d'une nouvelle version de Perl en écrivant :

 
Sélectionnez
use 5.6.0;

5-6. Quantificateurs

À moins que vous n'en décidiez autrement, chaque élément d'une expression régulière ne correspond qu'une fois à quelque chose. Avec un motif comme /nop/, chacun de ces caractères doit correspondre, l'un à la suite de l'autre. Les mots comme « panoplie » ou « xénophobie » marchent bien, car la correspondance se fait n'a aucune importance.

Si vous vouliez trouver à la fois « xénophobie » et « Snoopy », vous ne pourriez pas utiliser le motif /nop/, puisque cela requiert qu'il n'y ait qu'un seul « o » entre le « n » et le « p », et que Snoopy en a deux. C'est ici que les quantificateurs montrent leur utilité : ils indiquent combien de fois quelque chose peut correspondre, au lieu de une fois par défaut. Les quantificateurs dans une expression régulière sont comme les boucles dans un programme ; en fait, si vous regardez une expression régulière comme un programme, alors ce sont des boucles. Certaines boucles sont exactes, comme « répète cette correspondance cinq fois seulement » ({5}). D'autres vous donnent à la fois la limite inférieure et la limite supérieure du nombre de correspondances, comme « répète ceci au moins deux fois, mais pas plus de quatre » ({2,4}). D'autres n'ont pas de limite supérieure du tout, comme « trouve ceci au moins deux fois, mais ensuite autant de fois que tu veux » ({2,}).

Le tableau 5-12 liste les quantificateurs que Perl reconnaît dans un motif.

Tableau 5-12. Comparaison des quantificateurs de motifs

Maximum

Minimum

Plage autorisée

{MIN,

MAX} {MIN, MAX}?

Doit apparaître au moins MIN fois, mais pas plus de MAX fois

{MIN,}

{MIN,}?

Doit apparaître au moins MIN fois

{COMPTE}

{COMPTE}?

Doit apparaître exactement COMPTE fois

*

*?

0 ou plus (comme {0,})

+

+?

1 fois ou plus (comme {1,})

?

??

0 ou 1 fois (comme {0,1})

Quelque chose suivi d'un * ou d'un ? n'est pas obligé de correspondre à tout prix. C'est parce que cela peut correspondre 0 fois et toujours être considéré comme un succès. Un + est souvent un meilleur choix, car l'élément recherché doit être présent au moins une fois.

Ne vous laissez pas embrouiller par l'emploi de « exactement » dans le tableau précédent. Il ne fait référence qu'au nombre de répétitions, et pas à la chaîne complète. Par exemple, $n =~ /\d{3}/ ne dit pas « est-ce que cette chaîne fait exactement trois chiffres de long ? ». En fait le motif se préoccupe de savoir s'il existe une position dans $n à laquelle trois chiffres apparaissent les uns à la suite des autres. Des chaînes comme « 101 rue de la République » correspondent, tout comme des chaînes telles que « 75019 » ou « 0 825 827 829 ». Toutes contiennent trois chiffres à la suite à un ou plusieurs endroits, ce qui est tout ce que vous demandiez. Voir dans la section Positions l'utilisation des assertions de position (comme dans /^\d{3}$/) pour préciser votre recherche.

Si on leur donne la possibilité de correspondre un nombre variable de fois, les quantificateurs maximaux vont essayer de maximiser le nombre de répétitions. C'est pourquoi quand nous disons « autant de fois que tu veux », les quantificateurs gourmands le comprennent comme « le plus de fois que tu le peux », à la seule condition que cela n'empêche pas des spécifications plus loin dans l'expression de correspondre. Si un motif contient deux quantificateurs ouverts, les deux ne peuvent évidemment pas consommer l'intégralité de la chaîne : les caractères utilisés par la première partie de l'expression ne sont plus disponibles pour la partie qui suit. Chaque quantificateur est gourmand aux dépens de ceux qui le suivent, en lisant le motif de gauche à droite.

C'est du moins le comportement traditionnel des quantificateurs dans les expressions régulières. Cependant Perl vous permet de modifier le comportement de ses quantificateurs : en mettant un ? après un quantificateur, vous le transformez de maximal en minimal. Cela ne signifie pas qu'un quantificateur minimal va toujours correspondre au nombre minimum de répétitions permises par ses spécifications, pas plus qu'un quantificateur maximal correspond toujours au plus grand nombre défini dans ses spécifications. La correspondance globale doit toujours se faire, et le quantificateur minimal prendra juste ce qu'il faut pour que ce soit un succès, et pas plus. (Les quantificateurs minimaux préfèrent la satisfaction à la gourmandise.)

Par exemple dans la correspondance :

 
Sélectionnez
"exigence" =~ /e(.*)e/ # $1 vaut maintenant "xigenc"

le .* correspond à « xigenc », la plus longue chaîne possible à laquelle il peut correspondre. (Il stocke également cette valeur dans $1, comme cela est expliqué dans la section Capture et regroupement plus loin dans ce chapitre.) Bien qu'une correspondance plus courte fut possible, un quantificateur gourmand s'en moque. S'il a le choix entre deux possibilités à partir de la même position, il retournera toujours la plus longue des deux.

Comparez ceci avec cela :

 
Sélectionnez
"exigence" =~ /e(.*?)e/ # $1 vaut maintenant "xig"

Ici, c'est la version minimale, .*?, qui est utilisée. L'ajout du ? à l'* fait que *? a un comportement opposé : ayant le choix entre deux possibilités à partir du même point, il prend toujours la plus courte des deux.

Bien que vous puissiez lire *? comme une injonction à correspondre à zéro ou plus de quelque chose en préférant zéro, cela ne veut pas dire qu'il correspondra toujours à zéro caractère. Si c'était le cas ici, par exemple, et qu'il laissait $1 à la valeur "", alors le second

« e » ne serait pas trouvé, puisqu'il ne suit pas immédiatement le premier.

Vous pourriez aussi vous demander pourquoi, alors qu'il cherchait une correspondance minimale avec /e(.*?)e/, Perl n'a pas plutôt mis « nc » dans $1. Après tout, « nc » se trouve lui aussi entre deux e et est plus court que « xig ». En Perl, le choix minimal/maximal se fait seulement lorsqu'on recherche la plus courte ou la plus longue parmi plusieurs correspondances ayant toutes le même point de départ. Si plusieurs correspondances possibles existent, mais commencent à des endroits différents dans la chaîne, alors leurs longueurs n'ont aucune importance — pas plus que le fait que vous ayez utilisé un quantificateur minimal ou maximal. La première de plusieurs correspondances possibles est toujours prépondérante devant celles qui la suivent. C'est seulement lorsque plusieurs correspondances possibles démarrent du même point dans la chaîne que le côté minimal ou maximal de la correspondance sert à les départager. Si les points de départ sont différents, il n'y a rien à départager. Les correspondances de Perl sont normalement de type la plus à gauche la plus longue ; avec une correspondance minimale, cela devient la plus à gauche la plus courte. Mais la partie la plus à gauche ne change pas et reste le critère dominant.(75)

Il y a deux manières de passer l'orientation à gauche du détecteur de motif. Premièrement vous pouvez utiliser un quantificateur gourmand au début (typiquement .*) pour essayer de consommer le début de la chaîne. En cherchant la correspondance pour un quantificateur gourmand, il essaie d'abord la correspondance la plus longue ce qui provoque effectivement une recherche de droite à gauche dans le reste de la chaîne :

 
Sélectionnez
"exigence" =~ /.*e(.*?)e/ # $1 vaut maintenant "nc"

Mais faites attention avec cette méthode, car la correspondance complète contient maintenant toute la chaîne jusqu'à ce point.

La seconde manière de défaire cette inclination à gauche est d'utiliser les assertions de position, qui sont discutées dans la section suivante.

5-7. Positions

Certaines constructions de regex représentent des positions dans la chaîne à traiter, qui est juste un emplacement à gauche ou à droite d'un véritable caractère. Ces métasymboles sont des exemples d'assertions de largeur nulle. Nous les appellerons souvent simplement « assertions ». (On les connaît aussi sous le nom d'« ancres », car elles lient une partie du motif à une position particulière.)

Vous pouvez toujours manipuler les positions dans une chaîne sans utiliser de motif. La fonction intégrée substr vous permet d'extraire et d'affecter des sous-chaînes, calculées à partir du début de la chaîne, de la fin de la chaîne ou à partir d'une position numérique particulière. C'est juste ce dont vous avez besoin si vous travaillez avec des enregistrements de longueur fixe. Les motifs deviennent nécessaires quand un décalage numérique n'est plus suffisant. Et la plupart du temps, les décalages ne sont pas suffisants — disons pas suffisamment commodes, par rapport aux motifs.

5-7-a. Les débuts : assertions \A et ^

L'assertion \A correspond seulement au début de la chaîne, quelles que soient les circonstances. Cependant l'assertion ^ est l'assertion traditionnelle de début de ligne, ainsi que celle de début de chaîne. C'est pourquoi si le motif utilise le modificateur /m(76) et que la chaîne contient des sauts de ligne, ^ correspond également n'importe où dans la chaîne immédiatement après un caractère de saut de ligne :

 
Sélectionnez
/\Abar/     # Correspond à "bar" et "barbecue"
/^bar/      # Correspond à "bar" et "barbecue"
/^bar/m     # Correspond à "bar" et "barbecue" et "zinc\nbar"

Utilisée en même temps que /g, le modificateur /m permet à ^ de correspondre plusieurs fois dans la même chaîne :

 
Sélectionnez
s/^\s+//gm;             # Supprime les blancs en tête de chaque ligne
$total++ while /^./mg;  # Compte les lignes qui ne sont pas vides
5-7-b. Fins : assertions \z, \Z et $

Le métasymbole \z correspond à la fin de la chaîne, quoi qu'il y ait à l'intérieur. \Z correspond juste avant le saut de ligne en fin de chaîne s'il y en a un, ou à la fin s'il n'y en a pas. Le métacaractère $ signifie en général la même chose que \Z. Cependant, si le modificateur /m a été spécifié et que la chaîne contient des sauts de ligne, alors $ peut aussi correspondre n'importe où dans la chaîne, juste avant un saut de ligne :

 
Sélectionnez
/bot\z/      # Correspond avec "robot"
/bot\Z/      # Correspond avec "robot" et "poulbot\n"
/bot$/       # Correspond avec "robot" et "poulbot\n"
/bot$/m      # Correspond avec "robot" et "poulbot\n" et "robot\nménager"
/^robot$/    # Correspond avec "robot" et "robot\n"
/^robot$/m   # Correspond avec "robot" et "robot\n" et "ce\nrobot\n"
/\Arobot\Z/  # Correspond avec "robot" et "robot\n"
/\Arobot\z/  # Correspond avec "robot" uniquement -- mais pourquoi ne
             #pas avoir utilisé eq ?

Tout comme avec ^, le modificateur /m permet à $ de correspondre plusieurs fois dans la même chaîne quand il est utilisé avec /g. (Ces exemples supposent que vous avez lu un enregistrement multiligne dans $_, par exemple en mettant $/ à "" avant la lecture.)

 
Sélectionnez
s/\s*$//gm;     # Supprime les blancs en fin de chaque ligne du paragraphe
while (/^([^:]+):\s*(.*)/gm ) { # obtient les en-têtes de mail
    $headers{$1} = $2;
}

Plus loin dans ce chapitre, à la section Interpolation de variables, nous discuterons de la façon dont vous pouvez interpoler des variables dans des motifs : si $toto vaut « bc », alors /a$toto/ est équivalent à /abc/. Ici, le $ ne correspond pas à la fin de la chaîne. Pour qu'un $ corresponde à la fin de la chaîne, il doit être à la fin du motif ou être immédiatement suivi d'une barre verticale ou d'une parenthèse fermante.

5-7-c. Limites : assertions \b et \B

L'assertion \b correspond à toute limite de mot, celle-ci étant définie comme la position entre un caractère \w et un caractère \W, dans n'importe quel ordre. Si l'ordre est \W\w c'est la limite en début de mot, et si l'ordre est \w\W c'est la limite en fin de mot. (Une extrémité de chaîne compte comme un caractère \W ici.) L'assertion \B correspond à toute position qui n'est pas une limite de mot, c'est-à-dire au milieu de \w\w ou de \W\W.

 
Sélectionnez
/\best\b/   # correspond à "ce qu'il est" et "à l'est d'Eden"
/\Best\B/   # correspond à "zeste" et "intestin"
/\best\B/   # correspond à "estival" et "il t'estime"
/\Best\b/   # correspond à "al  et "ouest puis nord"

Comme \W comprend tous les caractères de ponctuation, (sauf le souligné), il y a des limites \b au milieu de chaînes comme « aujourd'hui », « booktech@oreilly.com »,

« S.N.C.F. » et « clef/valeur ».

À l'intérieur d'une classe de caractères ([\b]), un \b représente un caractère espace arrière plutôt qu'une limite de mot.

5-7-d. Reconnaissance progressive

Utilisée avec le modificateur /g, la fonction pos vous permet de connaître ou de modifier la position à partir de laquelle la prochaine comparaison progressive commencera :

 
Sélectionnez
$voleur = "Bilbon Baggins";
while ($voleur =~ /b/gi) {
    printf "Trouvé un B en %d\n", pos($voleur)-1;
}

(Nous retirons un de la position, car c'est la longueur de la chaîne que nous voulions, alors que pos renvoie toujours la position juste après la fin de la reconnaissance précédente.)

Le code ci-dessus affiche :

 
Sélectionnez
Trouvé un B en 0
Trouvé un B en 3
Trouvé un B en 7

Après un échec, la position de reconnaissance est normalement remise à zéro. Si vous utilisez le modificateur /c (pour « continue »), alors quand le /g se termine, l'échec de la reconnaissance ne réinitialise pas le pointeur de position. Cela vous permet de continuer votre recherche au-delà de ce point, sans recommencer à partir du début.

 
Sélectionnez
$voleur = "Bilbon Baggins";
while ($voleur =~ /b/gci) { # AJOUT DE /c
    printf "Trouvé un B en %d\n", pos($voleur)-1;
}
while ($voleur =~ /i/gi) {
    printf "Trouvé un I en %d\n", pos($voleur)-1;
}

En plus des trois B trouvés précédemment, Perl indique maintenant qu'il a trouvé un i à la position 11. Sans le /c, la seconde boucle de reconnaissance aurait recommencé depuis le début de la chaîne et d'abord trouvé un autre i à la position 1.

5-7-e. Là où vous en étiez : assertion \G

Dès que vous commencez à réfléchir en termes de la fonction pos, il est tentant de commencer à creuser dans votre chaîne à coups de substr, mais c'est rarement la bonne chose à faire. La plupart du temps, si vous avez commencé avec une recherche de motif, vous devriez continuer avec une recherche de motif. Cependant, si vous recherchez une assertion de position, c'est probablement \G qu'il vous faut.

L'assertion \G représente à l'intérieur du motif le même point que pos représente à l'extérieur. Quand vous faites une recherche progressive avec le modificateur /g (ou que vous avez utilisé la fonction pos pour sélectionner directement le point de départ), vous pouvez utiliser \G pour spécifier la position qui suit la fin de la reconnaissance précédente. C'est-à-dire qu'il reconnaît la position immédiatement avant le caractère qui serait identifié par pos. Cela vous permet de vous souvenir où vous en étiez :

 
Sélectionnez
($recette = <<'DISH') =~ s/^\s+//gm;
    # Anchois sauce Roswel
    Préchauffer le four à 451 deg. fahrenheit.
    Mélanger 1 ml. de dilithium avec 3 oz. de
    NaCl et y plonger 4 anchois. Glacer avec
    1 gr. de mercure. Faire cuire 4 heures et
    laisser refroidir 3 secondes. Pour 10 martiens.
DISH

$recette =~ /\d+ /g;
$recette =~ /\G(\w+)/;      # $1 vaut "deg"
$recette =~ /\d+ /g;
$recette =~ /\G(\w+)/;      # $1 vaut "ml"
$recette =~ /\d+ /g;
$recette =~ /\G(\w+)/;      # $1 vaut "gr"

Le métasymbole \G est souvent utilisé dans une boucle, comme nous allons le montrer dans notre prochain exemple. Nous faisons une « pause » après chaque suite de chiffres, et à cette position, nous testons s'il y a une abréviation. Si oui, nous récupérons les deux mots qui suivent. Sinon, nous récupérons seulement le mot suivant :

 
Sélectionnez
pos($recette) = 0; # Par sécurité, initialise \G à 0
while ( $recette =~ /(\d+) /g ) {
    my $quantite = $1;
    if ($recette =~ / \G (\w{0,3}) \. \s+ de \s+ (\w+) /x) { # abrév. + mot
        print "$quantite $1 de $2\n";
    } else {
        $recette =~ / \G (\w+) /x; # juste un mot
        print "$quantite $1\n";
    }
}

Ce qui donne :

 
Sélectionnez
451 deg
1 ml de dilithium
3 oz de NaCl
4 anchois
1 gr de mercure
4 heures
3 secondes
10 martiens

5-8. Capture et regroupement

Les motifs vous permettent de regrouper des portions de votre motif dans des sous-motifs et de vous souvenir des chaînes reconnues par ces sous-motifs. Nous appellerons le premier comportement regroupement et le second capture.

5-8-a. Capture

Pour capturer une sous-chaîne pour une utilisation ultérieure, mettez des parenthèses autour du motif qui la reconnaît. La première paire de parenthèses stocke sa sous-chaîne dans $1, la deuxième paire dans $2 et ainsi de suite. Vous pouvez utiliser autant de parenthèses que vous voulez ; Perl continuera à définir des variables numérotées pour que vous puissiez représenter ces chaînes capturées.

Quelques exemples :

 
Sélectionnez
/(\d)(\d)/ # Trouve deux chiffres, qui sont capturés dans $1 et $2
/(\d+)/ # Trouve un ou plusieurs chiffres, capturés ensemble dans $1
/(\d)+/ # Trouve un chiffre une fois ou plus, et capture le dernier dans $1

Remarquez la différence entre le deuxième et le troisième motifs. La deuxième forme est en général ce que vous voulez. La troisième forme ne crée pas plusieurs variables pour plusieurs chiffres. Les parenthèses sont numérotées quand le motif est compilé, pas quand il est utilisé.

Les chaînes capturées sont souvent appelées références arrière, car elles font référence à des parties du texte situées en arrière par rapport à la position courante. Il existe en fait deux manières d'utiliser ces références arrière. Les variables numérotées que vous avez vues donnent accès en dehors du motif aux références arrière, mais à l'intérieur du motif, cela ne marche pas. Vous devez utiliser \1, \2, etc.(77) Pour trouver les mots doublés comme « le le » ou « est est », vous pourriez utiliser ce motif :

 
Sélectionnez
/\b(\w+) \1\b/i

Mais la plupart du temps, vous utiliserez la forme $1, car en général on applique un motif, pour ensuite faire quelque chose des sous-chaînes. Supposons que vous ayez du texte (des en-têtes de mail) qui ressemble à ça :

 
Sélectionnez
From: gnat@perl.com
To: camelot@oreilly.com
Date: Mon, 17 Jul 2000 09:00:00 -1000
Subject: Eye of the needle
et que vous vouliez construire un hachage qui fasse le

lien entre le texte avant les deux-points et celui après. Si vous boucliez sur ce texte ligne à ligne (par exemple parce que vous lisez un fichier), vous pourriez faire comme suit :

 
Sélectionnez
while (<>) {
    /^(.*?): (.*)$/; #   Texte avant les deux-points dans $1, après dans $2
    $champs{$1} = $2;
}

Comme $`, $& et $'', ces variables numérotées sont à portée dynamique jusqu'à la fin du bloc ou de la chaîne eval englobant, ou jusqu'à la prochaine recherche de motif réussie, selon lequel se produit le premier. Vous pouvez également les utiliser dans la partie droite (la zone de remplacement) d'une substitution :

 
Sélectionnez
s/^(\S+) (\S+)/$2 $1/; # Intervertit les deux premiers mots

Les regroupements peuvent s'emboîter, et quand ils le font, les groupes sont comptés par l'ordre de leurs parenthèses ouvrantes. Donc, si on donne la chaîne « Primula Brandebouc » au motif :

 
Sélectionnez
/^((\w+) (\w+))$/

il capturerait « Primula Brandebouc » dans $1, « Primula » dans $2, et « Brandebouc » dans $3. Cela est décrit à la figure 5-1.

Image non disponible
Figure 5-1. Création de références arrière avec des parenthèses

Les motifs avec capture sont souvent utilisés en contexte de liste pour remplir une liste de valeurs, car le motif est assez malin pour retourner les sous-chaînes comme une liste :

 
Sélectionnez
($premier, $dernier)  = /^(\w+) (\w+)$/;
($tout, $premier, $dernier) =