perlop - Opérateurs Perl et priorité
Le tableau suivant présente les opérateurs Perl et leur priorité, du plus prioritaire au moins prioritaire. Remarquez que tous les opérateurs empruntés au C gardent la même relation de priorité entre eux même lorsque ces priorités sont légèrement tordues. (Cela rend plus simple l'apprentissage de Perl par les programmeurs C.) À quelques exceptions près, tous ces opérateurs agissent sur des valeurs scalaires et pas sur des tableaux de valeurs.
gauche termes et opérateurs de listes (leftward)
gauche ->
nonassoc ++ --
droite **
droite ! ~ \ et + et - unaires
gauche =~ !~
gauche * / % x
gauche + - .
gauche << >>
nonassoc opérateurs nommés unaires
nonassoc < > <= >= lt gt le ge
nonassoc == != <=> eq ne cmp
gauche &
gauche | ^
gauche &&
gauche ||
nonassoc .. ...
droite ?:
droite = += -= *= etc.
gauche , =>
nonassoc opérateurs de listes (rightward)
droite not
gauche and
gauche or xor
Dans les sections qui suivent, ces opérateurs sont présentés par ordre de priorité.
De nombreux opérateurs peuvent être redéfinis pour des objets. Voir overload.
Un TERME a la plus haute priorité en Perl. Cela inclut les variables, les apostrophes et autres opérateurs style apostrophe, les expressions entre parenthèses et n'importe quelle fonction dont les arguments sont donnés entre parenthèses. En fait, ce ne sont pas vraiment des fonctions, juste des opérateurs de listes et des opérateurs unaires qui se comportent comme des fonctions parce que vous avez mis des parenthèses autour des arguments. Tout cela est documenté dans la page de manuel perlfunc.
Si un opérateur de liste (print(), etc.) ou un opérateur unaire (chdir(), etc.) est directement suivi par une parenthèse gauche, l'opérateur et les arguments entre parenthèses se voient attribués la priorité la plus haute exactement comme un appel de fonction.
En l'absence de parenthèses, la priorité des opérateurs de liste comme print, sort ou chmod n'est ni très haute ni très basse et dépend de ce qu'il y a à gauche et/ou à droite de l'opérateur. Par exemple, dans:
@ary = (1, 3, sort 4, 2);
print @ary; # affiche 1324
la virgule à droite du tri (sort) est évaluée avant le tri (sort) alors que la virgule de gauche est évaluée après. En d'autres termes, un opérateur de listes a tendance à manger tous les arguments qui le suit et à se comporter comme un simple TERME par rapport à l'expression qui le précède:
# L'évaluation de exit a lieu avant le print !
print($foo, exit); # Pas vraiment ce que vous vouliez.
print $foo, exit; # Ni dans ce cas.
# L'évaluation de print a lieu avant le exit.
(print $foo), exit; # C'est ce que vous voulez.
print($foo), exit; # Ici aussi.
print ($foo), exit; # Et encore dans ce cas.
Remarquez aussi que :
print ($foo & 255) + 1, "\n";
ne donne probablement pas ce que vous attendiez à priori. Voir les Opérateurs unaires nommés pour plus de détails.
Sont aussi reconnus comme des termes les constructions do {} et eval {}, les appels à des subroutines ou à des méthodes ainsi que les
constructeurs anonymes [] et {}.
Voir aussi Opérateurs apostrophe et type apostrophe à la fin de cette section mais aussi Les opérateurs d'E/S.
Comme en C et en C++, ``->'' est un opérateur de déréférencement infixe. Si du côté droit on trouve
soit [...] soit {...} alors le côté gauche doit être une référence en dur ou symbolique à un
tableau ou à une table de hachage (ou un emplacement relié à une référence
en dur si c'est une lvalue (une valeur affectable)). Voir la page de manuel perlref.
Par contre, si le côté droit est un nom de méthode ou une simple variable scalaire contenant un nom de méthode alors le côté gauche doit être soit un objet (une référence bénie -- NdT: on dit blessed en anglais) soit un nom de classe (c'est à dire un nom de package). Voir la page de manuel perlobj.
``++'' et ``--'' fonctionne comme en C. Placés avant la variable, ils incrémentent ou décrémentent la variable avant de retourner sa valeur. Placés après, ils incrémentent ou décrémentent la variable après avoir retourner sa valeur.
De plus, l'opérateur d'auto-incrémentation inclut un comportement magique.
Si vous incrémentez une variable numérique ou qui a déjà été utilisée dans
un contexte numérique, vous obtenez l'incrément normal. Si, par contre, la
variable n'a été utilisée que dans un contexte de chaîne et correspond au
motif /^[a-zA-Z]*[0-9]*$/, l'incrémentation a lieu sur la chaîne elle-même en préservant les
caractères dans leur intervalle et en gérant les éventuelles retenues :
print ++($foo = '99'); # affiche '100'
print ++($foo = 'a0'); # affiche 'a1'
print ++($foo = 'Az'); # affiche 'Ba'
print ++($foo = 'zz'); # affiche 'aaa'
L'opérateur d'auto-décrémentation n'est pas magique.
L'opérateur binaire ``**'' est l'opérateur puissance. Remarquez qu'il est
plus prioritaire que le moins unaire donc -2**4 signifie -(2**4) et non pas
(-2)**4. (Il est implémenté par la fonction C pow(3) qui
travaille sur des doubles en interne.)
L'opérateur unaire ``!'' est la négation logique, i.e. ``non''. Voir aussi not
pour une version moins prioritaire de cet opération.
L'opérateur unaire ``-'' est la négation arithmétique si l'opérande est
numérique. Si l'opérande est un identificateur, il retourne une chaîne
constituée du signe moins suivi de l'identificateur. Si la chaîne commence
par un plus ou un moins, la valeur retournée est la chaîne commençant par
le signe opposé. L'un des effets de ces règles est que -bareword est équivalent à
"-bareword".
L'opérateur unaire ``~'' effectue la négation bit à bit, i.e. le complément
à 1. Par exemple 0666 &~ 027 vaut 0640. (Voir aussi Arithmétique entière
et Opérateurs bit à bit sur chaînes.)
L'opérateur unaire ``+'' n'a aucun effet même sur les chaînes. Il est pratique pour séparer syntaxiquement un nom de fonction d'une expression entre parenthèses qui autrement aurait été interprétée comme la liste complète des arguments de la fonction. (Voir les exemples de Termes et opérateurs de listes (leftward).)
L'opérateur unaire ``\'' crée une référence vers ce qui le suit. Voir la page de manuel perlref. Ne confondez pas ce comportement avec celui de la barre oblique inversée (backslash) à l'intérieur d'une chaîne bien que les deux formes proposent une notion de protection contre l'interprétation de ce qui les suit.
L'opérateur binaire ``=~'' applique un motif de reconnaissance à une
expression scalaire. Plusieurs opérations cherchent ou modifient la chaîne
$_ par défaut. Cet opérateur permet d'appliquer cet sorte
d'opérations à d'autres chaînes. L'argument de droite est le motif de
recherche, de substitution ou de remplacement. L'argument de gauche est ce
qui est supposé être cherché, substitué ou remplacé à la place de la valeur
par défaut $_. La valeur retournée indique le succès de l'opération. (Si
l'argument de droite est une expression plutôt qu'un motif de recherche, de
substitution ou de remplacement, il est interprété comme un motif de
recherche lors de l'exécution. Cela peut être moins efficace qu'une
recherche explicite puisque le motif doit être compilé à chaque fois que
l'expression est évaluée.)
L'opérateur binaire ``!~'' est exactement comme ``=~'' sauf que la valeur retournée est le contraire au sens logique.
L'opérateur binaire ``*'' multiplie deux nombres.
L'opérateur binaire ``/'' divise deux nombres.
L'opérateur binaire ``%'' calcule le modulo de deux nombres. Soit deux
opérandes entiers donnés $a et $b : si $b est positif alors $a % $b vaut
$a moins le plus grand multiple de $b qui n'est pas plus grand que
$a. Si $b est négatif alors $a % $b vaut $a moins le plus petit multiple de $b qui n'est pas plus petit que $a (i.e. le résultat est plus petit ou égal à zéro). Remarquez que lorsque
vous êtes dans la portée de
use integer, ``%'' vous donne accès directement à l'opérateur modulo tel qu'il est
défini par votre compilateur C. Cet opérateur n'est pas très bien défini
pour des opérandes négatifs mais il s'exécute plus rapidement.
L'opérateur binaire ``x'' est l'opérateur de répétition. Dans un contexte scalaire, il retourne une chaîne constituée de son opérande de gauche répété le nombre de fois spécifié par son opérande de droite. Dans un contexte de liste, si l'opérande de gauche est une liste, il répète la liste.
print '-' x 80; # affiche une ligne de '-'
print "\t" x ($tab/8), ' ' x ($tab%8); # tab over
@ones = (1) x 80; # une liste de quatre-vingt 1.
@ones = (5) x @ones; # place tous les éléments à 5.
L'opérateur binaire ``+'' retourne la somme de deux nombres.
L'opérateur binaire ``-'' retourne la différence de deux nombres.
L'opérateur binaire ``.'' concatène deux chaînes.
L'opérateur binaire ``<<`` retourne la valeur de son opérande de gauche décalée vers la gauche d'un nombre de bits spécifié par son opérande de droite. Les arguments devraient être des entiers. (Voir aussi Arithmétique entière.)
L'opérateur binaire ``>>'' retourne la valeur de son opérande de gauche décalée vers la droite d'un nombre de bits spécifié par son opérande de droite. Les arguments devraient être des entiers. (Voir aussi Arithmétique entière.)
Les différents opérateurs unaire nommés sont traités comme des fonctions à
un argument avec des parenthèses optionnelles. Ceci inclut les opérateurs
de tests de fichiers comme -f, -M, etc. Voir la page de manuel perlfunc.
Si un opérateur de liste (print(), etc.) ou un opérateur unaire (chdir(), etc.) est suivi d'une parenthèse ouvrante, l'opérateur et ses arguments entre parenthèses sont considérés comme de priorité la plus haute exactement comme n'importe quel appel à une fonction. Exemples :
chdir $foo || die; # (chdir $foo) || die
chdir($foo) || die; # (chdir $foo) || die
chdir ($foo) || die; # (chdir $foo) || die
chdir +($foo) || die; # (chdir $foo) || die
mais puisque * est plus prioritaire que || :
chdir $foo * 20; # chdir ($foo * 20)
chdir($foo) * 20; # (chdir $foo) * 20
chdir ($foo) * 20; # (chdir $foo) * 20
chdir +($foo) * 20; # chdir ($foo * 20)
rand 10 * 20; # rand (10 * 20)
rand(10) * 20; # (rand 10) * 20
rand (10) * 20; # (rand 10) * 20
rand +(10) * 20; # rand (10 * 20)
Voir aussi Termes et opérateurs de listes (leftward).
L'opérateur binaire ``<'' renvoie vrai si son opérande gauche est numériquement plus petit que son opérande droit.
L'opérateur binaire ``>'' renvoie vrai si son opérande gauche est numériquement plus grand que son opérande droit.
L'opérateur binaire ``<='' renvoie vrai si son opérande gauche est numériquement plus petit ou égal que son opérande droit.
L'opérateur binaire ``>='' renvoie vrai si son opérande gauche est numériquement plus grand ou égal que son opérande droit.
L'opérateur binaire ``lt'' renvoie vrai si son opérande gauche est alphabétiquement plus petit que son opérande droit.
L'opérateur binaire ``gt'' renvoie vrai si son opérande gauche est alphabétiquement plus grand que son opérande droit.
L'opérateur binaire ``le'' renvoie vrai si son opérande gauche est alphabétiquement plus petit ou égal que son opérande droit.
L'opérateur binaire ``ge'' renvoie vrai si son opérande gauche est alphabétiquement plus grand ou égal que son opérande droit.
L'opérateur binaire ``=='' renvoie vrai si l'opérande gauche est numériquement égal à l'opérande droit.
L'opérateur binaire ``!='' renvoie vrai si l'opérande gauche n'est pas numériquement égal à l'opérande droit.
L'opérateur binaire ``<=>`` renvoie -1, 0 ou 1 selon que l'opérande gauche est numériquement et respectivement plus petit, égal ou plus grand que l'opérande droit.
L'opérateur binaire ``eq'' renvoie vrai si l'opérande gauche est égal alphabétiquement à l'opérande droit.
L'opérateur binaire ``ne'' renvoie vrai si l'opérande gauche n'est pas égal alphabétiquement à l'opérande droit.
L'opérateur binaire ``cmp'' renvoie -1, 0 ou 1 selon que l'opérande gauche est alphabétiquement et respectivement plus petit, égal ou plus grand que l'opérande droit.
``lt'', ``le'', ``ge'', ``gt'' et ``cmp'' utilise l'ordre de tri
(collation) spécifié par le locale courant si use locale est actif. Voir perllocale.
L'opérateur binaire ``&'' renvoie le résultat d'un ET bit à bit entre ses opérandes. (Voir aussi Arithmétique entière et Opérateurs bit à bit sur chaînes.)
L'opérateur binaire ``|'' renvoie le résultat d'un OU bit à bit entre ses deux opérandes. (Voir aussi Arithmétique entière et Opérateurs bit à bit sur chaînes.)
L'opérateur binaire ``^'' renvoie le résultat d'un OU EXCLUSIF bit à bit entre ses deux opérandes. (Voir aussi Arithmétique entière et Opérateurs bit à bit sur chaînes.)
L'opérateur binaire ``&&'' calcule un ET logique rapide. Cela signifie que si l'opérande gauche est faux, l'opérande droit n'est même pas évalué. Le contexte scalaire ou liste se propage vers l'opérande droit si il est évalué.
L'opérateur binaire ``||'' calcule un OU logique rapide. Cela signifie que si l'opérande gauche est vrai, l'opérande droit n'est même pas évalué. Le contexte scalaire ou liste se propage vers l'opérande droit si il est évalué.
Les opérateurs || et && diffèrent du C sur un point: au lieu de renvoyer 0 ou 1, ils renvoient la
dernière valeur évaluée. Donc, un moyen raisonnablement portable de trouver
le répertoire home (en supposant que ce ne soit pas ``0'') peut être :
$home = $ENV{'HOME'} || $ENV{'LOGDIR'} ||
(getpwuid($<))[7] || die "You're homeless!\n";
En particulier, cela signifie que vous ne devriez pas les utiliser pour choisir entre deux agrégats dans une affectation :
@a = @b || @c; # c'est pas bon
@a = scalar(@b) || @c; # voila ce que ça signifie
@a = @b ? @b : @c; # cela marche très bien par contre
Pour remplacer d'une manière plus lisible l'usage de && et || pour contrôler un flot d'opérations, Perl propose les opérateurs and et or
(voir plus bas). Le comportement d'évaluation rapide est le même. Par
contre, la priorité de ``and'' et ``or'' est plus basse, vous pouvez donc
les utiliser après les opérateurs de listes sans ajouter de parenthèses :
unlink "alpha", "beta", "gamma"
or gripe(), next LINE;
Avec les opérateurs à la C, vous auriez dû l'écrire :
unlink("alpha", "beta", "gamma")
|| (gripe(), next LINE);
En revanche, l'utilisation de ``or'' lors d'une affectation ne donne pas ce que vous voulez; voir plus bas.
L'opérateur binaire ``..'' est l'opérateur d'intervalle qui est en fait
deux opérateurs totalement différents selon le contexte. Dans un contexte
de liste, il renvoie un tableau de valeurs commençant à la valeur de son
opérande gauche et se terminant à la valeur de son opérande droit (par pas
de 1). C'est pratique pour écrire des boucles foreach (1..10) et pour des opérations sur des tableaux. Dans l'implémentation actuelle,
aucun tableau temporaire n'est généré lorsque l'opérateur d'intervalle est
utilisé comme expression de boucles foreach mais les vieilles versions de Perl peuvent consommer énormément de mémoire
lorsque vous écrivez quelque chose comme :
for (1 .. 1_000_000) {
# code
}
Dans un contexte scalaire, ``..'' renvoie une valeur booléenne. L'opérateur
est bi-stable comme un interrupteur et simule l'opérateur d'intervalle de
ligne (virgule) de sed, de awk et d'autres éditeurs. Chaque opérateur ``..'' conserve en mémoire son
propre état booléen. Il reste faux tant que son opérande gauche est faux.
Puis dès que son opérande gauche devient vrai, il reste vrai jusqu'à ce que
son opérande droit soit vrai. APRÈS quoi, l'opérateur d'intervalle redevient faux. (Il ne redevient faux que le
prochaine fois que l'opérateur d'intervalle est évalué. Il peut tester
l'opérande droit et devenir faux lors de la même évaluation où il devient
vrai (comme dans awk) mais il retournera encore une fois vrai. Si vous ne voulez pas qu'il
teste l'opérande droit avant la prochaine évaluation (comme dans sed), utilisez trois points (``...'') à la place de deux.) L'opérande droit
n'est pas évalué tant que l'opérateur est dans l'état ``faux'' et
l'opérande gauche n'est pas évalué tant que l'opérateur est dans l'état
``vrai''. La priorité de l'opérateur intervalle est plus basse que || et
&&. La valeur retournée est soit la chaîne vide pour signifier
``faux'' soit un numéro de séquence (commençant à 1) pour ``vrai''. Le
dernier numéro de séquence d'un intervalle est suivi de la chaîne ``E0'' ce
qui ne perturbe pas sa valeur numérique mais vous donne quelque chose à
chercher afin d'exclure cette dernière valeur. Vous pouvez exclure la
première valeur en demandant une valeur supérieure à 1. Si l'un des
opérandes de l'opérateur intervalle pris dans un contexte scalaire est une
expression constante, cette opérande est implicitement comparé à la
variable $. (le numéro de ligne courant). Exemples :
Comme opérateur scalaire :
if (101 .. 200) { print; } # affiche la seconde centaine de lignes
next line if (1 .. /^$/); # saut des lignes d'en-têtes
s/^/> / if (/^$/ .. eof()); # place le corps comme citation
# analyse d'e-mail
while (<>) {
$in_header = 1 .. /^$/;
$in_body = /^$/ .. eof();
# faire quelque chose avec...
} continue {
close ARGV if eof; # réinitialisation de $. à chaque fichier
}
Comme opérateur de liste :
for (101 .. 200) { print; } # affiche $_ 100 fois
@foo = @foo[0 .. $#foo]; # un no-op coûteux
@foo = @foo[$#foo-4 .. $#foo]; # extraction des 5 derniers items
L'opérateur intervalle (dans un contexte de liste) utilise l'algorithme magique d'auto-incrémentation si les opérandes sont des chaînes. Vous pouvez écrire :
@alphabet = ('A' .. 'Z');
pour obtenir toutes les lettres de l'alphabet ou
$hexdigit = (0 .. 9, 'a' .. 'f')[$num & 15];
pour obtenir les chiffres héxadécimaux ou
@z2 = ('01' .. '31'); print $z2[$mday];
pour obtenir des dates avec des zéros. Si la valeur finale donnée n'est pas dans la séquence produite par l'incrémentation magique, la séquence continue jusqu'à ce que la prochaine valeur soit plus longue que la valeur finale spécifiée.
L'opérateur ``?:'' est l'opérateur conditionnel exactement comme en C. Il travaille presque comme un si-alors-sinon. Si l'argument avant le ? est vrai, l'argument avant le : est renvoyé sinon l'argument après le : est renvoyé. Par exemple :
printf "J'ai %d chien%s.\n", $n,
($n == 1) ? '' : "s";
Le contexte scalaire ou de liste se propage au second ou au troisième argument quelque soit le choix.
$a = $ok ? $b : $c; # un scalaire
@a = $ok ? @b : @c; # un tableau
$a = $ok ? @b : @c; # oups, juste un compte !
On peut affecter quelque chose à l'opérateur si le second ET le troisième arguments sont des lvalues légales (ce qui signifie qu'on peut leur affecter quelque chose) :
($a_or_b ? $a : $b) = $c;
Il n'y a aucune garantie que cela contribue à la lisibilité de votre programme.
Puisque l'opérateur produit un résultat affectable, l'usage d'affectation sans parenthèse peut amener quelques problèmes. Par exemple, le ligne suivante:
$a % 2 ? $a += 10 : $a += 2
signifie réellement :
(($a % 2) ? ($a += 10) : $a) += 2
au lieu de :
($a % 2) ? ($a += 10) : ($a += 2)
``='' est l'opérateur habituel d'affectation.
Les opérateurs d'affectation fonctionnent comme en C. Donc :
$a += 2;
est équivalent à :
$a = $a + 2;
quoique sans dupliquer les éventuels effets de bord que le déréférencement
de la lvalue pourraient déclencher, comme par exemple avec
tie(). Les autres opérateurs d'affectation fonctionnent de la
même manière. Voici ceux qui sont reconnus :
**= += *= &= <<= &&=
-= /= |= >>= ||=
.= %= ^=
x=
Remarque: bien que regroupés par famille, tous ces opérateurs ont la priorité de l'affectation.
Au contraire du C, les opérateurs d'affectation produisent une lvalue valide. Modifier une affectation est équivalent à faire l'affectation puis à modifier la variable qui vient d'être affectée. C'est très pratique pour modifier une copie de quelque chose comme dans :
($tmp = $global) =~ tr [A-Z] [a-z];
De même :
($a += 2) *= 3;
est équivalent à :
$a += 2;
$a *= 3;
L'opérateur binaire ``,'' est l'opérateur virgule. Dans un contexte scalaire, il évalue son opérande gauche et jette le résultat puis il évalue son opérande droit et retourne cette valeur. C'est exactement comme l'opérateur virgule du C.
Dans un contexte de liste, c'est tout simplement le séparateur d'arguments de la liste. Il insère ses deux opérandes dans la liste.
Le digramme => est un synonyme de l'opérateur virgule. C'est pratique pour indiquer que les arguments fonctionnent par paires. Depuis la version 5.001, il contraint le mot à sa gauche à être interprété comme une chaîne.
Du côté droit d'un opérateur de liste, il a un priorité très basse qui permet de maîtriser toutes les expressions présentes séparées par des virgules. Les seuls opérateurs de priorité inférieure sont les opérateurs logiques ``and'', ``or'' et ``not'' qui peuvent être utiliser pour évaluer plusieurs appels à des opérateurs de liste sans l'ajout de parenthèses supplémentaires :
open HANDLE, "filename"
or die "Can't open: $!\n";
Voir aussi la discussion sur les opérateurs de liste dans Termes et opérateurs de liste (leftward).
L'opérateur unaire ``not'' renvoie la négation logique de l'expression à sa droite. Il est équivalent à ``!'' sauf sa priorité beaucoup plus basse.
L'opérateur binaire ``and'' renvoie la conjonction logique des deux expressions qui l'entourent. Il est équivalent à && sauf sa priorité beaucoup plus basse. Cela signifie qu'il est rapide: i.e., l'expression de droite est évaluée uniquement si celle de gauche est vraie.
L'opérateur binaire ``or'' renvoie la disjonction logique des deux expressions qui l'entourent. Il est équivalent à || sauf sa priorité beaucoup plus basse. C'est pratique pour contrôler une suite d'opérations :
print FH $data or die "Can't write to FH: $!";
Cela signifie qu'il est rapide: i.e., l'expression de droite est évaluée uniquement si celle de gauche est fausse. À cause de sa priorité, vous devriez l'éviter dans les affectations et ne l'utiliser que pour du contrôle de suite d'opérations.
$a = $b or $c; # bug: c'est pas bon
($a = $b) or $c; # voila ce que ça signifie
$a = $b || $c; # il vaut mieux l'écrire ainsi
Au contraire, lorsque l'affectation est dans un contexte de liste et que vous voulez utilisez ``||'' pour du contrôle, il vaut mieux utiliser ``or'' pour que l'affectation soit prioritaire.
@info = stat($file) || die; # holà, sens scalaire de stat !
@info = stat($file) or die; # meilleur, @info reçoit ce qu'il faut
Bien sûr, il est toujours possible d'utiliser les parenthèses.
L'opérateur binaire ``xor'' renvoie le ou exclusif des deux expressions qui l'entourent. Il ne peut évidemment pas être rapide.
Voici ce qui existe en C et que Perl n'a pas:
Opérateur adresse-de. (Voir l'opérateur ``\'' pour prendre une référence.)
Opérateur contenu-de ou de déréférencement. (Les opérateurs de déréférencement de Perl sont des préfixes typés: $, @, % and &.)
Opérateur de conversion de type (de cast).
Bien qu'habituellement nous pensions aux apostrophes (et autres guillemets)
pour des valeurs littérales, en Perl, elles fonctionnent comme des
opérateurs et proposent différents types d'interpolation et des capacités
de reconnaissance de motif. Perl fournit des caractères standards pour cela
mais fournit aussi le moyen de choisir vos propres caractères. Dans la
table suivante, le {}
représente n'importe quelle paire de délimiteurs que vous aurez choisie.
Les délimiteurs qui ne marchent pas par deux utilisent le même caractère au
début et à la fin par contre les quatre sortes de parenthèses (parenthèses,
crochets, accolades et inférieur/supérieur) marchent par deux et peuvent
être imbriquées.
Standard Générique Signification Interpolation
'' q{} Littérale non
"" qq{} Littérale oui
`` qx{} Commande oui (sauf si délimiteur '')
qw{} Liste de mots non
// m{} Reconnaissance de motif oui (sauf si délimiteur '')
qr{} Motif oui (sauf si délimiteur '')
s{}{} Substitution oui (sauf si délimiteur '')
tr{}{} Translittération non (voir plus bas)
Remarquez qu'il peut y avoir des espace entre l'opérateur et le caractère
délimiteur sauf lorsque # est utilisé. q#foo# est interprété comme la chaîne foo alors que q #foo# est l'opérateur q suivi d'un commentaire. Ses arguments sont alors pris sur la ligne
suivante. Cela permet d'écrire :
s {foo} # Remplace foo
{bar} # par bar.
Pour les constructions qui réalisent une interpolation, les variables
commençant par ``$'' ou ``@'' sont interpolées ainsi que les séquences suivantes. Dans une
translittération, seules les dix premières séquences sont utilisables.
\t tabulation (HT, TAB)
\n nouvelle ligne (LF, NL)
\r retour chariot (CR)
\f page suivante (FF)
\a alarme (bip) (BEL)
\e escape (ESC)
\033 caractère en octal (ESC)
\x1B caractère hexadécimal (ESC)
\c[ caractère de contrôle
\l converti en minuscule le caractère suivant
\u converti en majuscule le caractère suivant
\L converti en minuscule jusqu'au prochain \E
\U converti en majuscule jusqu'au prochain \E
\E fin de modification de casse
\Q désactive les méta-caractères de motif jusqu'au prochain \E
Si use locale est actif, la table de majuscules/minuscules utilisée par
\l, \L, \u et \U est celle du locale courant. Voir perllocale.
Tous les systèmes utilisent le "\n" virtuel pour représenter une terminaison de ligne appelée ``newline'' ou
``nouvelle ligne''. Il n'existe pas de caractère physique invariant pour
représenter ce caractère ``newline''. C'est une illusion qu'essayent
conjointement de maintenir le système d'exploitation, les pilotes de
périphériques et Perl. Tous les systèmes ne lisent pas "\r" comme le CR ASCII ni "\n" comme le LF ASCII. Par exemple, sur Mac, ils sont inversés et sur des
systèmes sans terminaison de ligne, écrire "\n" peut ne produire aucun donnée. En général, utilisez "\n" lorsque vous pensez ``newline'' pour votre système mais utilisez le
littéral ASCII quand voulez un caractère exact. Par exemple, de nombreux
des protocoles réseaux attendent et préfèrent un CR+LF (("\012\015" ou "\cJ\cM") comme terminaison de ligne. La plupart acceptent juste "\012" et tolère rarement juste "\015". Si vous prenez l'habitude d'utiliser "\n" sur les réseaux, vous aurez des problèmes un jour.
Vous ne pouvez pas inclure littéralement les caractères $ et @ à l'intérieur d'une séquence \Q. Tels quels, ils se référeraient à la variable correspondante. Précédés
d'un \, ils correspondraient à la chaîne
\$ ou \@. Vous êtes obligés d'écrire quelque chose comme
m/\Quser\E\@\Qhost/.
Les motifs sont sujets à un niveau supplémentaire d'interprétation en tant
qu'expression régulière. C'est fait lors d'une seconde passe après
l'interpolation des variables si bien qu'une expression régulière peut-être
introduite dans un motif via une variable. Si ce n'est pas ce que vous
voulez, utilisez \Q pour interpoler une variable littéralement.
Mis à part ce qui précède, il n'y pas de multiples niveaux d'interpolation. En particulier et contrairement à ce qu'attendraient des programmeurs shell, les accents graves (ou backticks) NE sont PAS interpolées à l'intérieur des guillemets et les apostrophes n'empêchent pas l'évaluation des variables à l'intérieur des guillemets.
Nous discuterons ici des opérateurs style apostrophe qui implique une reconnaissance de motif ou une action s'y rapportant.
La plus grande partie de cette section est relative à l'utilisation des expressions régulières en Perl. Cette utilisation peut être considérée de deux points de vue : Perl fournit une chaîne et un ``motif'' au moteur de RE (Expression Régulière) pour une reconnaissance, le moteur RE trouve (ou non) une correspondance et Perl utilise le résultat de moteur RE pour ses propres opérations en demandant éventuellement au moteur de trouver d'autres correspondances.
Le moteur RE n'a aucune idée de ce que Perl compte faire avec ce qu'il trouve et de manière similaire le reste de Perl n'a aucune idée du sens que le moteur RE accorde à une expression régulière donnée. Cela permet une séparation propre et dans cette section nous ne parlons des reconnaissances que du point de vue de Perl. L'autre point de vue est discuté dans la page de manuel perlre.
C'est la même chose qu'une recherche par /motif/ sauf qu'elle ne marche qu'une seule fois entre chaque appel à l'opérateur
reset(). C'est une optimisation pratique si vous ne recherchez
que la première occurrence de quelque chose dans chaque fichier ou groupes
de fichiers par exemple. Seuls les motifs ?? locaux au package courant sont réinitialisés par reset().
while (<>) {
if (?^$?) {
# ligne blanche entre en-tête et corps
}
} continue {
reset if eof; # réinitialisation de ?? pour le fichier suivant
}
Cet usage est vaguement désapprouvé et peut être supprimé dans une version future de Perl.
Effectue le recherche d'un motif d'expression régulière dans une chaîne et,
dans un contexte scalaire, renvoie vrai (1) ou faux (''). Si aucune chaîne
n'est spécifiée via l'opérateur =~ ou l'opérateur !~, c'est dans la chaîne $_ que s'effectue la recherche. (La
chaîne spécifiée par =~ n'est pas nécessairement une lvalue -- cela peut être le résultat de
l'évaluation d'une expression.) Voir aussi la page de manuel perlre. Voir perllocale pour des informations supplémentaires qui s'appliquent lors de l'usage de use
locale.
Les options (ou modificateurs) sont :
c Ne pas réinitialiser la position de recherche lors d'un échec avec /g.
g Recherche globale, i.e. trouver toutes les occurrences.
i Reconnaissance de motif indépendamment de la casse (majuscules/minuscules).
m Traitement de chaîne comme étant multi-lignes.
o Compilation du motif uniquement la première fois.
s Traitement de la chaîne comme étant une seule ligne.
x Utilisation des expressions régulières étendues.
Si ``/'' est le délimiteur alors le m initial est optionnel. Avec le m
vous pouvez utiliser n'importe quelle paire de caractères ni alphanumérique
ni blancs comme délimiteur. C'est particulièrement pratique pour les
chemins d'accès Unix qui contiennent des ``/'' afin d'éviter le LTS
(leaning toothpick syndrome). Si ``?'' est le délimiteur alors la règle
``ne-marche-qu-une-fois'' de ?MOTIF? s'applique.
MOTIF peut contenir des variables qui seront interpolées (et le motif
recompilé) chaque fois que la recherche du motif est effectuée sauf si le
délimiteur choisi est une apostrophe. (Remarquez que $) et $| ne peuvent pas être interpolés puisqu'ils ressemblent à des tests de fin de
chaîne.) Si vous voulez qu'un motif ne soit compilé qu'une seule fois,
ajoutez /o après le dernier délimiteur. Ceci évite des coûteuses recompilations lors
de l'exécution et c'est utile lorsque le valeur interpolée ne doit pas
changer pendant la durée de vie du script. Par contre, ajouter /o suppose que vous ne changerez pas la valeur des variables présentes dans le
motif. Si vous les changez, Perl ne s'en apercevra même pas.
Si l'évaluation du MOTIF est la chaîne vide, la dernière expression régulière reconnue est utilisée à la place.
Si l'option /g n'est pas utilisée, dans un contexte de liste m//
retourne une liste constituée de toutes les sous-motifs reconnus par des
parenthèses dans le motif, i.e. ($1, $2, $3...). (Remarquez que
$1, $2, etc. sont aussi mises à jours ce qui diffère du comportement de Perl 4.)
Lorsqu'il n'y a pas de parenthèses dans le motif, la valeur retournée est
la liste (1) en cas de succès. Qu'il y ait ou non de parenthèses, une liste vide est
retournée en cas d'échec.
Exemples :
open(TTY, '/dev/tty');
<TTY> =~ /^y/i && foo(); # appel de foo si désiré
if (/Version: *([0-9.]*)/) { $version = $1; }
next if m#^/usr/spool/uucp#;
# le grep du pauvre
$arg = shift;
while (<>) {
print if /$arg/o; # compile une seule fois
}
if (($F1, $F2, $Etc) = ($foo =~ /^(\S+)\s+(\S+)\s*(.*)/))
Ce dernier exemple découpe $foo en trois parties (le deux
premiers mots et le reste de la ligne) et les affecte à $F1,
$F2 et $etc. La condition est vraie si l'une des variables est
affectée, i.e. si le motif est reconnu.
Le modificateur /g spécifie une recherche globale du motif -- c'est à dire la recherche
d'autant de correspondances que possible dans la chaîne. Le comportement
dépend du contexte. Dans un contexte de liste, c'est la liste de toutes les
sous-chaînes reconnues par les sous-motifs (entre parenthèses) du motif qui
est retournée. En l'absence de parenthèses, c'est la liste de toutes les
chaînes correspondant au motif qui est retournée, comme si il y avait des
parenthèses autour du motif lui-même.
Dans un contexte scalaire, chaque exécution de m//g trouve la prochaine correspondance et retourne TRUE si il y a
correspondance et FALSE si il n'y en a plus. La position après la dernière
correspondance peut-être lue et modifiée par la fonction
pos(); voir pos. Normalement, un échec de la recherche réinitialise la position de
recherche au début de la chaîne sauf si vous ajouter le modificateur /c (e.g. m//gc). La modification de la chaîne sur laquelle à lieu la recherche
réinitialise aussi la position de recherche.
Vous pouvez mélanger des reconnaissances m//g avec des m/\G.../g où
\G est l'assertion de longueur nulle qui est reconnue à la position exacte où,
si elle existe, s'est arrêtée la précédente recherche m//g. L'assertion
\G n'est pas acceptable sans le modificateur /g ; dans la version courante, en l'absence de /g, il se trouve que \G se comporte exactement comme \A mais c'est accidentel et cela peut changer dans les version futures.
Exemples :
# contexte de liste
($one,$five,$fifteen) = (`uptime` =~ /(\d+\.\d+)/g);
# contexte scalaire
{
local $/ = "";
while (defined($paragraph = <>)) {
while ($paragraph =~ /[a-z]['")]*[.!?]+['")]*\s/g) {
$sentences++;
}
}
}
print "$sentences\n";
# utilisation de m//gc avec \G
$_ = "ppooqppqq";
while ($i++ < 2) {
print "1: '";
print $1 while /(o)/gc; print "', pos=", pos, "\n";
print "2: '";
print $1 if /\G(q)/gc; print "', pos=", pos, "\n";
print "3: '";
print $1 while /(p)/gc; print "', pos=", pos, "\n";
}
Le dernier exemple devrait afficher :
1: 'oo', pos=4
2: 'q', pos=5
3: 'pp', pos=7
1: '', pos=7
2: 'q', pos=8
3: '', pos=8
Une construction idiomatique pratique pour des analyseurs à la lex est
/\G.../gc. Vous pouvez combiner plusieurs expressions régulières de ce type pour
traiter une chaîne partie par partie en faisant différentes actions selon
l'expression qui est reconnue. Chaque expression essaye de correspondre là
où la précédente s'est arrêtée.
$_ = <<'EOL';
$url = new URI::URL "http://www/"; die if $url eq "xXx";
EOL
LOOP:
{
print(" chiffres"), redo LOOP if /\G\d+\b[,.;]?\s*/gc;
print(" minuscule"), redo LOOP if /\G[a-z]+\b[,.;]?\s*/gc;
print(" MAJUSCULE"), redo LOOP if /\G[A-Z]+\b[,.;]?\s*/gc;
print(" Capitalisé"), redo LOOP if /\G[A-Z][a-z]+\b[,.;]?\s*/gc;
print(" MiXtE"), redo LOOP if /\G[A-Za-z]+\b[,.;]?\s*/gc;
print(" alphanumérique"), redo LOOP if /\G[A-Za-z0-9]+\b[,.;]?\s*/gc;
print(" autres"), redo LOOP if /\G[^A-Za-z0-9]+/gc;
print ". C'est tout !\n";
}
Voici la sortie (découpée en plusieurs lignes) :
autres minuscule autres minuscule MAJUSCULE autres
MAJUSCULE autres minuscule autres minuscule autres
minuscule minuscule autres minuscule minuscule autres
MiXtE autres. C'est tout !
Une apostrophe pour une chaîne littérale. Un backslash (une barre oblique inverse) représente un backslash sauf si il est suivi par le délimiteur ou par un autre backslash auquel cas le délimiteur ou le backslash sont interpolés.
$foo = q!I said, "You said, 'She said it.'"!;
$bar = q('This is it.');
$baz = '\n'; # une chaîne de deux caractères
Des guillemets pour une chaîne interpolée.
$_ .= qq
(*** La ligne précédente contient le gros mot "$1".\n)
if /(tcl|rexx|python)/; # :-)
$baz = "\n"; # une chaîne d'un caractère
Opérateur ``comme-une-expression-regulière''. CHAINE est interpolée de la même manière que MOTIF dans m/MOTIF/. Si ``''' est utilisé comme délimiteur, aucune interpolation de variables n'est effectuée. Renvoie une expression Perl qui peut être utilisée à la place de l'expression /CHAINE/imosx.
Par exemple :
$rex = qr/ma.CHAINE/is;
s/$rex/foo/;
est équivalent à :
s/ma.CHAINE/foo/is;
Le résultat peut être utilisé comme motif dans une recherche de correspondance :
$re = qr/$motif/;
$string =~ /foo${re}bar/; # peut être interpolée dans d'autres motifs
$string =~ $re; # ou utilisée seule
$string =~ /$re/; # ou de cette manière
Puisque Perl peut compiler le motif lors de l'exécution de l'opérateur
qr(), l'utilisation de qr() peut augmenter les
performances dans quelques situations, notamment si le résultat de qr() est utilisé seul
:
sub match {
my $patterns = shift;
my @compiled = map qr/$_/i, @$patterns;
grep {
my $success = 0;
foreach my $pat @compiled {
$success = 1, last if /$pat/;
}
$success;
} @_;
}
La précompilation du motif en une représentation interne lors du
qr() évite une recompilation à chaque fois qu'une recherche /$pat/ est tentée. (Notez que Perl a de nombreuses autres optimisations internes
amis aucune n'est déclenchée dans l'exemple précédent si nous n'utilisons
pas l'opérateur qr().)
Les options sont :
i Motif indépendant de la casse (majuscules/minuscules).
m Traitement de chaîne comme étant multi-lignes.
o Compilation du motif uniquement la première fois.
s Traitement de la chaîne comme étant une seule ligne.
x Utilisation des expressions régulières étendues.
Voir la page de manuel perlre pour de plus amples informations sur la syntaxe correcte de CHAINE et pour une description détaillée de la sémantique des expressions régulières.
Une chaîne qui est (éventuellement) interpolée puis exécutée comme une
commande système par /bin/sh ou équivalent. Les jokers, tubes (pipes) et redirections sont pris en
compte. L'ensemble de la sortie standard de la commande est renvoyé ; la
sortie d'erreur n'est pas affectée. Dans un contexte scalaire, le résultat
est retourné comme une seule chaîne (potentiellement multi-lignes). Dans un
contexte de liste, le résultat est une liste de lignes (quelque soit la
définition des lignes donnée par $/ ou $INPUT_RECORD_SEPARATOR).
Puisque les apostrophes inverses (backticks) n'affectent pas la sortie
d'erreur, il vous faut utiliser la (les) syntaxe(s) de
redirection du shell (en supposant qu'elle(s) existe(nt)) afin
de capter les erreurs. Pour récupérer STDOUT et STDERR d'une commande :
$output = `cmd 2>&1`;
Pour récupérer STDOUT et faire disparaître STDERR :
$output = `cmd 2>/dev/null`;
Pour récupérer STDERR et faire disparaître STDOUT (l'ordre est ici très important) :
$output = `cmd 2>&1 1>/dev/null`;
Pour échanger STDOUT et STDERR afin de récupérer STDERR tout en laissant STDOUT s'afficher normalement :
$output = `cmd 3>&1 1>&2 2>&3 3>&-`;
Pour récupérer STDOUT et STDERR séparément, il est plus simple et sain de les rediriger séparément vers des fichiers qui seront lus lorsque l'exécution de la commande sera terminée :
system("program args 1>/tmp/program.stdout 2>/tmp/program.stderr");
L'utilisation de l'apostrophe comme délimiteur protège la commande de l'interpolation normalement effectuée par Perl sur les chaînes entre guillemets. Le chaîne est passé tel quelle au shell :
$perl_info = qx(ps $$); # Le $$ de Perl
$shell_info = qx'ps $$'; # Le $$ du nouveau shell
Remarquez que la manière dont la chaîne est évaluée est entièrement
dépendant de l'interpréteur de commandes de votre système. Sur la plupart
des plateformes, vous aurez à protéger les méta-caractères du shell si vous
voulez qu'ils soient traités littéralement. En pratique, c'est difficile à
faire, surtout qu'il n'est pas toujours facile de savoir quels caractères
doivent être protégés. Voir la page de manuel perlsec pour un exemple propre et sûre d'utilisation manuelle de
fork() et exec() pour émuler proprement
l'utilisation des backticks.
Sur certaines plateformes (particulièrement celles style DOS) le shell peut
ne pas être capable de gérer des commandes multi-lignes. Dans ce cas,
l'usage de passages à la ligne dans la chaîne de commande ne donne pas ce
que vous voulez. Vous pouvez évaluez plusieurs commandes sur une seule et
même ligne et les séparant par le caractère de séparation de commandes si
votre shell le supporte (e.g. ; pour la plupart des shells Unix; & sur le cmd shell de Windows NT).
N'oubliez pas que certains shells ont quelques restrictions sur la longueur de la ligne de commande. Vous devez vous assurer que votre chaîne n'excède pas cette limite même après interpolation. Voir les notes spécifiques à votre plateforme pour plus de détails sur votre environnement particulier.
L'utilisation de cet opérateur peut aboutir à des programmes difficiles à
porter puisque les commandes shells appelées varient d'un système à l'autre
et peuvent parfois ne pas exister du tout. À titre d'exemple, la commande type
du shell POSIX est très différente de la commande type sous DOS. Cela ne signifie pas qu'il vous faut à tout prix éviter cet
opérateur lorsque c'est le bon moyen de faire ce que vous voulez. Perl a
été conçu pour être un ``glue language''... La seule chose qui compte c'est
que vous sachiez ce que vous faites.
Voir Les opérateurs d'E/S pour des plus amples informations.
Retourne la liste des mots extraits de CHAINE en utilisant les espaces comme délimiteurs de mots. C'est exactement équivalent à :
split(' ', q/CHAINE/);
Cette équivalence signifie que, utilisé dans un contexte scalaire, vous récupérez (malheureusement) le comportement scalaire de split accompagné d'un mystérieux avertissement (warning). Mais ne comptez pas là-dessus car, dans une prochaine version, cela sera peut-être changé pour être exactement équivalent à la liste :
('foo', 'bar', 'baz')
qui dans un contexte scalaire donnerait le résultat 'baz'.
Quelques exemples classiques :
use POSIX qw( setlocale localeconv )
@EXPORT = qw( foo bar baz );
Une erreur assez commune est de séparer les mots par des virgules ou de
mettre des commentaires dans une qw-chaîne multi-lignes. C'est la raison pour laquelle l'option -w produit des warning lorsque CHAINE contient le caractère ``,'' ou le
caractère ``#''.
Recherche le motif dans une chaîne puis, s'il est trouvé, le remplace par le texte de REMPLACEMENT et retourne finalement le nombre de substitutions effectuées. Sinon, renvoie faux (en l'espèce, la chaîne vide).
Si aucune chaîne n'est spécifiée via =~ ou !~, la recherche et la substitution s'appliquent à la variable $_. (La chaîne
spécifiée par =~
doit être une variable scalaire, un élément d'un tableau ou d'un table de
hachage ou une affectation de l'un ou de l'autre... en un mot, une lvalue.)
Si le délimiteur choisi est l'apostrophe, aucune interpolation n'est
effectuée ni sur MOTIF ni sur REMPLACEMENT. Sinon, si MOTIF contient un $
qui ressemble plus à une variable qu'à un test de fin de chaîne, la
variable sera interpolée dans le motif lors de l'exécution. Si vous voulez
que le motif ne soit compilé qu'une seule fois la première fois que la
variable est interpolée, utilisez l'option /o. Si l'évaluation du motif est la chaîne nulle, la dernière expression
régulière reconnue est utilisée à la place. Voir la page de manuel perlre pour de plus amples informations à ce sujet. Voir perllocale pour des informations supplémentaires qui s'appliquent lors de l'usage de use locale.
Les options sont :
e Évaluez la partie droite comme une expression.
g Substitution globale, i.e. toutes les occurrences.
i Motif indépendant de la casse (majuscules/minuscules).
m Traitement de chaîne comme étant multi-lignes.
o Compilation du motif uniquement la première fois.
s Traitement de la chaîne comme étant une seule ligne.
x Utilisation des expressions régulières étendues.
N'importe quel délimiteur (ni blanc ni alphanumérique) peut remplacer les
barres obliques (slash). Si l'apostrophe est utilisée, aucune interpolation
n'est effectuée sur la chaîne de remplacement (par contre, le modificateur /e
passe outre). Au contraire de Perl 4, Perl 5 considère l'accent grave
(backtick) comme un délimiteur normal ; le texte de remplacement n'est pas
évalué comme une commande. Si le MOTIF est délimité par des caractères
fonctionnant en paire (comme les parenthèses), le texte de REMPLACEMENT a
sa propre paire de délimiteurs qui peuvent être ou non des caractères
fonctionnant par paire, e.g. s(foo)(bar) ou
s<foo>/bar/. L'option /e implique que la partie remplacement sera interprétée comme une expression
Perl à part entière et donc évaluée comme telle. Par contre, sa validité
syntaxique est évaluée lors de la compilation.
Exemples:
s/\bgreen\b/mauve/g; # ne modifie pas wintergreen
$path =~ s|/usr/bin|/usr/local/bin|;
s/Login: $foo/Login: $bar/; # motif dynamique
($foo = $bar) =~ s/this/that/; # recopie puis modification
$count = ($paragraph =~ s/Mister\b/Mr./g); # calcul du nombre de substitution
$_ = 'abc123xyz';
s/\d+/$&*2/e; # produit 'abc246xyz'
s/\d+/sprintf("%5d",$&)/e; # produit 'abc 246xyz'
s/\w/$& x 2/eg; # produit 'aabbcc 224466xxyyzz'
s/%(.)/$percent{$1}/g; # pas de /e
s/%(.)/$percent{$1} || $&/ge; # une expression donc /e
s/^=(\w+)/&pod($1)/ge; # appel de fonction
# expansion des variables dans $_, mais dynamique uniquement
# en utilisant le déréférencement symbolique
s/\$(\w+)/${$1}/g;
# plusieurs /e peuvent être appliqués
# ceci développe toutes les variables scalaires incluses
# (même les lexicales) dans $_
s/(\$\w+)/$1/eeg;
# Suppression des commentaires C (presque tous).
$program =~ s {
/\* # Reconnais le début du commentaire
.*? # Reconnais un minimum de caractères
\*/ # Reconnais la fin de commentaire
} []gsx;
s/^\s*(.*?)\s*$/$1/; # suppression des espaces aux extrémités de $_ (couteux)
for ($variable) { # suppression des espaces aux extrémités de $_ (efficace)
s/^\s+//;
s/\s+$//;
}
s/([^ ]*) *([^ ]*)/$2 $1/; # inverse les deux premiers champs
Remarquez l'usage de $ à la place de \ dans le dernier exemple. À l'inverse de sed, nous utilisons la forme \<digit> uniquement dans la partie gauche. Partout ailleurs, c'est $<digit>.
Dans certains cas, il ne suffit pas de mettre /g pour modifier toutes les occurrences. Voici quelques cas communs :
# placer des virgules correctement dans un entier
# (NdT: en français, on mettrait des espaces)
1 while s/(.*\d)(\d\d\d)/$1,$2/g; # perl4
1 while s/(\d)(\d\d\d)(?!\d)/$1,$2/g; # perl5
# remplacement des tabulations par 8 espaces
1 while s/\t+/' ' x (length($&)*8 - length($`)%8)/e;
Substitue chacune des occurrences des caractères de la liste recherchée par
le caractère correspondant de la liste de remplacement. Cette opérateur
retourne le nombre de caractères remplacés ou supprimés. Si aucune chaîne
n'est spécifié via =~ ou !~, la substitution s'applique à $_. (La chaîne
spécifiée par =~ doit être une variable scalaire, un élément d'un tableau ou d'un table de
hachage ou une affectation de l'un ou de l'autre... en un mot, une lvalue.)
Un intervalle de caractères peut-être spécifié grâce à un tiret. Donc
tr/A-J/0-9/ effectue les mêmes remplacements que
tr/ACEGIBDFHJ/0246813579/. Pour les adeptes de sed, y est fourni comme un synonyme de tr. Si la liste recherchée est délimitée par des caractères fonctionnant par
paire (comme les parenthèses) alors la liste de remplacement a ses propres
délimiteurs, e.g. tr[A-Z][a-z] ou
tr(+\-*/)/ABCD/.
Remarquez aussi que le concept d'intervalle de caractères n'est pas vraiment portable entre différents codages -- et même dans un même codage, cela peut produire un résultat que vous n'attendez pas. Un bon principe de base est de n'utiliser que des intervalles qui commencent et se terminent dans le même alphabet ([a-e], [A-E]) ou dans les chiffres ([0-9]). Tout le reste n'est pas sûr. Dans le doute, énumérez l'ensemble des caractères explicitement.
Les options (ou modificateurs) :
c Complémente la SEARCHLIST.
d Efface les caractères trouvés mais non remplacés.
s Agrège les caractères de remplacement dupliqués.
Si le modificateur /c est utilisé, c'est le complément de la liste recherchée qui est utilisé. Si le modificateur /d est spécifié, tout caractère spécifié dans LISTERECHERCHEE et sans équivalent dans LISTEREMPLACEMENT est effacé. (Ceci est tout de même plus flexible que le comportement de certains programme tr qui efface tout ce qui est dans LISTERECHERCHEE, point!) Si le modificateur /s est spécifié, les suites de caractères qui sont remplacés par le même caractère sont agrégées en un seul caractère.
Si le modificateur /d est utilisé, LISTEREMPLACEMENT est toujours interprété exactement comme spécifié. Sinon, si LISTEREMPLACEMENT est plus court que LISTERECHERCHEE, le dernier caractère est répété autant de fois que nécessaire pour obtenir la même longueur. Si LISTEREMPLACEMENT est vide, LISTERECHERCHEE est utilisé à la place. Ce dernier point est très pratique pour comptabiliser les occurrences d'une classe de caractères ou pour agréger les suites de caractères d'une classe.
Exemples :
$ARGV[1] =~ tr/A-Z/a-z/; # tout en minuscule
$cnt = tr/*/*/; # compte les étoiles dans $_
$cnt = $sky =~ tr/*/*/; # compte les étoiles dans $sky
$cnt = tr/0-9//; # compte les chiffres dans $_
tr/a-zA-Z//s; # bookkeeper -> bokeper
($HOST = $host) =~ tr/a-z/A-Z/;
tr/a-zA-Z/ /cs; # remplace tous les non alphanumériques
# par un seul espace
tr [\200-\377]
[\000-\177]; # efface le 8ème bit.
Si plusieurs caractères de substitution sont donnés pour un caractère, seul le premier est utilisé :
tr/AAA/XYZ/
transformera tous les A par X.
Remarquez que puisque la table de substitution est construite lors de la
compilation, ni LISTERECHERCHEE ni LISTEREMPLACEMENT ne sont sujettes à
l'interpolation de chaîne entre guillemets. Cela signifie que si vous
voulez utiliser des variables, vous devez utiliser eval() :
eval "tr/$oldlist/$newlist/";
die $@ if $@;
eval "tr/$oldlist/$newlist/, 1" or die $@;
Face à quelque chose qui pourrait avoir différentes interprétations, Perl utilise le principe du FCQJP (qui signifie Faites Ce Que Je Pense - pas ce que j'écris) pour choisir l'interprétation la plus probable. Cette stratégie fonctionne tellement bien que les utilisateurs de Perl soupçonnent rarement l'ambiguïté de ce qu'ils écrivent. Par contre, de temps à autre, l'idée que se fait Perl diffère de ce que l'auteur du programme pensait.
L'objet de cette partie est de clarifier la manière dont Perl interprète les chaînes. La raison la plus fréquente pour laquelle quelqu'un a besoin de connaître tous ces détails est l'utilisation d'une expression régulière ``velue''. Par contre, les premiers pas de l'interprétation d'une chaîne sont les mêmes pour toutes les constructions de chaînes. Ils sont donc expliqués ensemble.
L'étape la plus importante de l'interprétation des chaînes dans Perl est la première exposée ci-dessous; lors d'une interprétation de chaînes, Perl cherche d'abord la fin de la construction. puis il interprète le contenu de cette construction. Si vous comprenez cette règle, vous pouvez sauter les autres étapes en première lecture. Contrairement à cette première étape, les autres étapes contredisent beaucoup moins souvent ce que l'utilisateur attend.
Quelques-unes des passes exposées plus loin sont effectuées simultanément. Mais comme le résultat est le même, nous les considérerons successivement une par une. Selon la construction, Perl effectue ou non certaines passes (de une à cinq) mais elles sont toujours appliquées dans le même ordre.
La première passe consiste à trouver la fin de la construction qui peut
être la suite de caractères "\nEOF\n" d'une construction <<EOF, le
/ qui termine une construction qq/, le ] qui termine une construction
qq[ ou le > qui termine un fileglob commencé par <.
Lors de la recherche d'un caractère délimiteur non appairé comme /, les combinaisons comme \\ et \/ sont sautées. Lors de la recherche d'un délimiteur appairé comme ], les combinaisons \\, \] et \[ sont sautées ainsi que les constructions imbriquées [ ]. Lors de la recherche d'une suite de caractère, rien n'est omis.
Pour les constructions en trois parties (s///, etc.), la recherche est répétée une fois supplémentaire.
Lors de cette recherche, aucune attention n'est accordée à la sémantique de la construction, donc :
"$hash{"$foo/$bar"}"
ou :
m/
bar # Ce n'est PAS un commentaire, ce slash / termine m// !
/x
ne constituent donc pas des constructions légales. L'expression se termine
au premier " ou / et le reste apparaît comme une erreur de syntaxe. Remarquez que puisque le
slash qui termine m// est suivi pas un
ESPACE, ce n'est pas une constructions m//x et donc # est interprété comme un # littéral.
Lors de la seconde passe, le texte entre le délimiteur de départ et le
délimiteur de fin est recopié à un endroit sûr et le \ des combinaisons constituées par un \ suivi du délimiteur (ou des délimiteurs si celui de départ diffère de celui
de fin) est supprimé.
Cette suppression n'a pas lieu pour des délimiteurs multi-caractères.
Remarquez que la combinaison \\ est laissée telle quelle.
À partir de ce moment plus aucune information concernant le(s)
délimiteur(s) n'est utilisée.
L'étape suivante est l'interpolation appliquée au texte isolé de ses délimiteurs. Il y a quatre cas différents.
<<'EOF', m'', s''', tr///, y///Aucune interpolation n'est appliquée.
'', q//
La seule interpolation est la suppression de \ des paires \\.
"", ``, qq//, qx//, <file*glob>
Les \Q, \U, \u, \L, \l (éventuellement associé avec \E) sont transformés en leur construction Perl correspondante et donc
"$foo\Qbaz$bar" est transformé en :
$foo . (quotemeta("baz" . $bar));
Les autres combinaisons constituées d'un \ suivi d'un ou plusieurs caractères sont remplacées par le ou les caractères
appropriés.
Soyez conscient que tout ce qui est entre \Q et \E est interpolé de manière classique. Donc "\Q\\E" ne contient pas de \E : il contient
\Q, \\ et E donc le résultat est le même que "\\\\E". Plus généralement, la présence de backslash entre \Q et \E aboutit à des résultats contre-intuitifs. Donc "\Q\t\E" est converti en :
quotemeta("\t")
qui est la même chose que "\\\t" (puisque TAB n'est pas alphanumérique). Remarquez aussi que :
$str = '\t'; return "\Q$str";
est peut-être plus proche de l'intention de celui qui écrit "\Q\t\E".
Les scalaires et tableaux interpolés sont convertis en construction join et
. et donc "'@arr'" devient :
"'" . (join $", @arr) . "'";
Toutes les étapes au-dessus sont effectuées simultanément de gauche à droite.
Puisque le résultat de ``\Q STRING \E'' a tous ses méta-caractères quotés,
il n'y aucun moyen d'insérer littéralement un $ ou un @ dans une paire
\Q\E : si il est protégé par \, un $ deviendra ``\\\$'' et sinon il sera interprété comme le début d'un scalaire
a interpolé.
Remarquez aussi que l'interpolation de code doit décider où se termine un
scalaire interpolé. Par exemple "a $b -> {c}" peut signifier :
"a " . $b . " -> {c}";
ou
"a " . $b -> {c};
Dans la plupart des cas, le choix est de considérer le texte le plus long possible n'incluant pas d'espaces entre ses composants et contenant des crochets ou accolades bien équilibrés. Puisque le résultat peut-être celui d'un vote entre plusieurs estimateurs heuristiques, le résultat n'est pas strictement prévisible mais il est habituellement correct pour les cas ambigus.
?RE?, /RE/, m/RE/, s/RE/foo/,
Le traitement des \Q, \U, \u, \L, \l et des interpolations est effectué quasiment de la même manière qu'avec les
constructions qq// sauf que la substitution d'un \ suivi d'un ou plusieurs caractères spéciaux
pour expressions régulières (incluant \) n'a pas lieu ! En outre, dans les constructions (?{BLOCK}), (?# comment ), et #-comment des
//x-expressions régulières, aucun traitement n'est effectué. C'est le premier
cas ou la présence de l'option //x est significative.
L'interpolation a quelques bizarreries: $|, $( et $) ne sont pas interpolés et les constructions comme $var[SOMETHING] peuvent être vues (selon le vote de plusieurs estimateurs différents) soit
comme un élément d'un tableau soit comme $var suivi par une alternative RE. C'est là où la notation ${arr[$bar]} prend tout sons intérêt : /${arr[0-9]}/ est interprété comme l'élément -9 du tableau et pas comme l'expression régulière contenu dans $arr suivie d'un chiffre (qui est l'interprétation de
/$arr[0-9]/). Puisqu'un vote entre différents estimateurs peut avoir lieu, le résultat n'est pas prévisible.
C'est à ce moment que la construction \1 est convertie en $1 dans le texte de remplacement de s///.
Remarquez que l'absence de traitement de \\ crée des restrictions spécifiques sur le texte après traitement : si le
délimiteur est /, on ne peut pas obtenir \/ comme résultat de cette étape (/ finirait l'expression régulière, \/ serait transformé en / par l'étape précédente et \\/ serait laissé tel quel). Puisque / est équivalent à \/ dans une expression régulière, ceci ne pose problème que lorsque le
délimiteur est un caractère spécial pour le moteur RE comme dans s*foo*bar*, m[foo], ou
?foo? ou si le délimiteur est un caractère alphanumérique comme dans :
m m ^ a \s* b mmx;
Dans l'expression régulière ci-dessus qui est volontairement obscure pour
l'exemple, le délimiteur est m, le modificateur est mx et après la suppression des backslash, l'expression est la même que m/ ^ a s* b /mx.
Cette étape est la dernière pour toutes les constructions sauf pour les expressions régulières qui sont traitées comme décrit ci-après.
Toutes les étapes précédentes sont effectuées lors de la compilation du code Perl alors que celle que nous décrivons ici l'est a priori lors de l'exécution (bien qu'elle soit parfois effectuée lors de la compilation pour des raisons d'optimisation). Après tous les pré-traitements précédents (et évaluation des éventuels concaténations, jointures, changements de casse et applications de quotemeta() déduits), la chaîne résultante est transmise au moteur RE pour compilation.
Ce qui se passe à l'intérieur du moteur RE est bien mieux expliqué dans la page de manuel perlre mais dans un souci de continuité, nous l'exposons un peu ici.
Voici donc une autre étape où la présence du modificateur //x est prise en compte. Le moteur RE explore la chaîne de gauche à droite et
la convertit en un automate à états finis.
Les caractères ``backslashés'' sont alors remplacés par la chaîne
correspondante (comme pour \{) ou génèrent des noeuds spéciaux dans l'automate à états finis (comme pour \b). Les caractères ayant un sens spécial pour le moteur RE (comme |) génèrent les noeuds ou les groupes de noeuds correspondants. Les
commentaires (?#...) sont ignorés. Tout le reste est converti en chaîne littérale à reconnaître
ou est ignoré (par exemple les espaces et les commentaires # si le modificateur //x est présent).
Remarquez que le traitement de la construction [...] est effectué en utilisant des règles complètement différentes du reste de
l'expression régulière. Le terminateur de cette construction est trouvé en
utilisant les mêmes règles que celles utilisées pour trouver le terminateur
d'une construction délimitée (comme {}). La seule exception est le ] qui suit immédiatement le [ et qui est considéré comme précédé d'un backslash. De manière similaire, la
construction (?{...}) n'est explorée que pour vérifier que les parenthèses, crochets et autres
accolades sont bien équilibrés.
Il est possible d'inspecter la chaîne envoyée au moteur RE ainsi que
l'automate résultant. Voir les arguments debug/debugcolor de la directive use re et/ou l'option Perl -Dr dans perlrun.
Cette étape n'est citée que dans un souci de complétude. Puisque elle ne change en rien la sémantique, les détails de cette étape ne sont pas documentés et sont susceptibles d'évoluer. Cette étape est appliqué à l'automate à états finis générés lors des étapes précédentes.
Par contre, dans de vieilles versions de Perl split était utilisé pour optimisé /^/ qui signifie /^/m. Ce comportement, bien qu'encore présent dans les versions actuelles de
Perl, sera peut-être déprécié dans le futur.
Il y a plusieurs opérateurs d'E/S (Entrée/Sortie) que vous devez connaître.
Une chaîne entourée d'apostrophes inversées (accents graves) subit tout
d'abord une substitution des variables exactement comme une chaîne entre
guillemets. Elle est ensuite interprétée comme une commande et la sortie de
cette commande est la valeur du pseudo-littéral comme avec un shell. Dans
un contexte scalaire, la valeur retournée est une seule chaîne constituée
de toutes les lignes de la sortie. Dans un contexte de liste, une liste de
valeurs est retournée, chacune des ces valeurs contenant une ligne de la
sortie. (Vous pouvez utilisez $/ pour utiliser un terminateur de ligne différent.) La commande est exécutée
à chaque fois que le pseudo-littéral est évalué. La valeur du statut de la
commande est retournée dans $? (voir
la page de manuel perlvar pour l'interprétation de $?). À l'inverse de csh, aucun traitement n'est appliqué aux valeurs retournées -- les passages à
la ligne restent des passages à la ligne. A l'inverse de la plupart des
shells, les apostrophes n'empêchent pas l'interprétation des noms de
variables dans la commande. Pour passer un $ au shell, vous devez le
protéger en le préfixant par un backslash (barre oblique inversée). La
forme générale des apostrophes inversées est qx//. (Puisque les apostrophes inversées impliquent toujours un passage par
l'interprétation du shell, voir la page de manuel perlsec pour tout ce qui concerne la sécurité.)
Dans un contexte scalaire, évaluer un filehandle entre supérieur/inférieur
produit le ligne suivante de ce fichier (saut à la ligne inclus si il y a
lieu) ou undef à la fin du fichier. Lorsque $/ a pour valeur undef
(i.e. le mode file slurp) et que le fichier est vide, '' est retourné lors de la première lecture puis ensuite undef.
Normalement vous devez affecter cette valeur à une variable mais il y a un
cas où une affectation automagique a lieu. Si et SEULEMENT si cette opérateur d'entrée est la seule chose présente dans la condition
d'une boucle while
ou for(;;) alors la valeur est automagiquement affectée à la variable
$_. Dans ces constructions, la valeur affectée (que ce soit automagiquement
ou explicitement) est ensuite testée pour savoir si elle est définie. Ce
test de définition évite les problèmes avec des lignes qui ont une valeur
qui pourrait être interprétée comme fausse par perl e.g. ``'' ou ``0'' sans
passage à la ligne derrière. (Cela peut vous sembler bizarre, mais vous
utiliserez de telles constructions dans la plupart des scripts Perl que
vous écrirez.) De ce fait, les lignes suivantes sont toutes équivalentes :
while (defined($_ = <STDIN>)) { print; }
while ($_ = <STDIN>) { print; }
while (<STDIN>) { print; }
for (;<STDIN>;) { print; }
print while defined($_ = <STDIN>);
print while ($_ = <STDIN>);
print while <STDIN>;
et celle-ci a un comportement similaire mais sans utiliser $_
:
while (my $line = <STDIN>) { print $line }
Si vous voulez réellement tester la valeur de la ligne pour terminer votre boucle, vous devrez la tester explicitement :
while (($_ = <STDIN>) ne '0') { ... }
while (<STDIN>) { last unless $_; ... }
Dans tous les autres contextes booléens, <filehandle> sans un test explicite de définition (par defined) déclenchera un warning si -w
est actif.
Les filehandles STDIN, STDOUT, et STDERR sont prédéfinis. (Les filehandles
stdin, stdout, et stderr fonctionnent aussi exceptés dans les packages où ils sont interprétés comme
des identifiants locaux.) Des filehandles supplémentaires peuvent être
créés par la fonction open(). Voir
open pour plus de détails.
Si <FILEHANDLE> est utilisé dans un contexte de liste, une liste constituée de toutes les lignes est retournée avec une ligne par élément de la liste. Il est facile d'utiliser de GRANDE quantité de mémoire par ce moyen. Donc, à utiliser avec précaution.
<FILEHANDLE> peut aussi être écrit readline(FILEHANDLE). Voir
readline.
Le filehandle vide <> est spécial et peut être utiliser pour émuler le comportement de sed et de awk. L'entrée de <> provient soit de l'entrée standard soit de tous les fichiers listés sur la
ligne de commande. Voici comment ça marche : la première fois que <> est évalué, on teste le tableau @ARGV et s'il est vide alors $ARGV[0] est positionné à ``-'' qui lorsqu'il sera ouvert lira l'entrée standard.
Puis le tableau @ARGV est traité comme une liste de nom de
fichiers. La boucle :
while (<>) {
... # code pour chaque ligne
}
est équivalent au pseudo-code Perl suivant :
unshift(@ARGV, '-') unless @ARGV;
while ($ARGV = shift) {
open(ARGV, $ARGV);
while (<ARGV>) {
... # code pour chaque ligne
}
}
sauf qu'il est moins volumineux et qu'en plus il marche. Il décale vraiment
le tableau @ARGV et stocke le nom du fichier courant dans la
variable $ARGV. Il utilise aussi en interne le filehandle ARGV -- <> est simplement un synonyme de <ARGV> qui est magique. (Le pseudo-code précédent ne fonctionne pas tout
simplement car il tient pas compte de l'aspect magique de
<ARGV>.)
Vous pouvez modifier @ARGV avant la premier <> tant que vous y laissez la liste des noms de fichiers que vous voulez. Le
numéro de ligne ($.) augmente exactement comme si vous aviez un seul gros fichier. (Voir
l'exemple de eof pour savoir comment le réinitialiser à chaque fichier.)
Si vous voulez affecter à @ARGV votre propre liste de
fichiers, procédez de la manière suivante. La ligne suivante positionne
@ARGV à tous les fichiers de type texte si aucun
@ARGV n'est fourni :
@ARGV = grep { -f && -T } glob('*') unless @ARGV;
Vous pouvez même les positionner avec des commandes pipe (tube). Par exemple, la ligne suivante applique automatiquement gzip aux arguments compressés :
@ARGV = map { /\.(gz|Z)$/ ? "gzip -dc < $_ |" : $_ } @ARGV;
Si vous voulez passer des options à votre script, vous pouvez utiliser l'un des modules Getopts ou placer au début de votre script une boucle du type :
while ($_ = $ARGV[0], /^-/) {
shift;
last if /^--$/;
if (/^-D(.*)/) { $debug = $1 }
if (/^-v/) { $verbose++ }
# ... # autres options
}
while (<>) {
# ... # code pour chaque ligne
}
Le symbole <> retournera undef à la fin des fichiers une seule fois. Si vous l'appelez encore une fois
après, il supposera que vous voulez traiter une nouvelle liste
@ARGV et, si vous n'avez pas rempli @ARGV, il traitera
l'entrée de STDIN.
Si la chaîne entre supérieur/inférieur est une référence à une variable
scalaire (e.g. <$foo>) alors cette variable doit contenir le nom du filehandle à utiliser comme
entrée ou son typeglob ou une référence vers un typeglob. Par exemple :
$fh = \*STDIN;
$line = <$fh>;
Si ce qui est entre supérieur/inférieur n'est ni un filehandle, ni une
simple variable scalaire contenant un nom de filehandle, un typeglob ou une
référence à un typeglob, il est alors interprété comme un motif de nom de
fichier à appliquer et ce qui est retourné est soit la liste de tous les
noms de fichiers ou juste le nom suivant dans la liste selon le contexte.
La distinction n'est faite que par la syntaxe. Cela signifie que <$x>
est toujours la lecture d'une ligne à partir d'un handle indirect mais que
<$hash{key}> est toujours un motif de nom de fichiers. Tout cela parce que
$x est une simple variable scalaire alors que $hash{key} ne l'est pas -- c'est un élément d'une table de hachage.
Comme pour les chaînes entre guillemets, un niveau d'interprétation est
appliqué au préalable mais vous ne pouvez pas dire <$foo> parce que c'est toujours interprété comme un filehandle indirect
(explications du paragraphe précédent). (Dans des version antérieures de
Perl, les programmeurs inséraient des accolades pour forcer
l'interprétation comme un motif de nom de fichier: <${foo}>. De nos jours, il est considéré plus propre d'appeler la fonction interne
explicitement par glob($foo) qui est probablement la meilleure façon de faire dans le premier cas.)
Exemple :
while (<*.c>) {
chmod 0644, $_;
}
est équivalent à :
open(FOO, "echo *.c | tr -s ' \t\r\f' '\\012\\012\\012\\012'|");
while (<FOO>) {
chop;
chmod 0644, $_;
}
En fait, c'est exactement la manière dont c'est implémenté actuellement.
(Ce qui signifie que cela ne marchera pas avec des noms de fichiers
contenant des espaces à moins que vous ayez csh(1) sur votre
machine.) Bien sûr, le moyen le plus court d'obtenir le résultat précédent
est :
chmod 0644, <*.c>;
Puisque les motifs de noms de fichiers invoquent un shell, il est souvent
plus rapide d'appeler readdir() vous-mêmes et d'appliquer
votre propre grep() aux noms de fichiers. En outre, la routine
glob() peut produire l'erreur ``Arg list too long'' (à moins
que vous ayez installé tcsh(1L) en tant que /bin/csh).
Un motif de noms de fichier évalue ses arguments uniquement lorsqu'il
débute une nouvelle liste. Toutes les valeurs doivent être lues avant de
recommencer. Dans un contexte de liste, cela n'a aucune importance puisque
vous récupérez toutes les valeurs quoi qu'il arrive. Dans un contexte
scalaire, par contre, l'opérateur retourne la valeur suivante à chaque fois
qu'il est appelé ou la valeur undef à la fin. Comme pour les filehandles un
defined automatique est généré lorsque l'opérateur est utilisé comme test d'un while ou d'un for - car sinon certains noms de fichier légaux (e.g. un fichier nommé 0) risquent de terminer la boucle. Encore une fois,
undef n'est retourné qu'une seule fois. Donc si vous n'attendez qu'une seule
valeur, il vaut mieux écrire :
($file) = <blurch*>;
que
$file = <blurch*>;
car dans le dernier cas vous aurez soit un nom de fichier soit FALSE.
Si vous avez besoin de l'interpolation de variables, il est définitivement
meilleur d'utiliser la fonction glob() parce que l'ancienne
notation peut être confondu avec la notation des filehandles indirects.
@files = glob("$dir/*.[ch]");
@files = glob($files[$i]);
Comme en C, Perl évalue un certain nombre d'expressions lors de la compilation lorsqu'il peut déterminer que tous les arguments d'un opérateur sont statiques et n'ont aucun effet de bord. En particulier, la concaténation de chaînes a lieu lors de la compilation entre deux littéraux qui ne sont pas soumis à l'interpolation de variables. L'interprétation du backslash (barre oblique inversée) a lieu elle aussi lors de la compilation. Vous pouvez dire :
'Now is the time for all' . "\n" .
'good men to come to.'
qui sera réduit à une seule chaîne de manière interne. Vous pouvez aussi dire :
foreach $file (@filenames) {
if (-s $file > 5 + 100 * 2**16) { }
}
le compilateur pré-calculera la valeur représentée par l'expression et l'interpréteur n'aura plus à le faite.
Les chaînes de bits de longueur arbitraire peuvent être manipulées par les
opérateurs bit à bit (~ | & ^).
Si les opérandes d'un opérateur bit à bit sont des chaînes de longueurs différentes, les opérateurs | et ^ agiront comme si l'opérande le plus court était complété par des bits à zéro à droite alors que l'opérateur and agira comme si l'opérande le plus long était tronqué à la longueur du plus court. Notez que la granularité pour de telles extensions ou troncations est d'un ou plusieurs octets.
# Exemples ASCII
print "j p \n" ^ " a h"; # affiche "JAPH\n"
print "JA" | " ph\n"; # affiche "japh\n"
print "japh\nJunk" & '_____'; # affiche "JAPH\n";
print 'p N$' ^ " E<H\n"; # affiche "Perl\n";
Si vous avez l'intention de manipuler des chaînes de bits, vous devez être
certain de fournir des chaînes de bits : si un opérande est un nombre cela
implique une opération bit à bit numérique. Vous pouvez explicitement préciser le type d'opération que vous attendez
en utilisant "" ou 0+
comme dans le exemples ci-dessous :
$foo = 150 | 105 ; # produit 255 (0x96 | 0x69 vaut 0xFF)
$foo = '150' | 105 ; # produit 255
$foo = 150 | '105'; # produit 255
$foo = '150' | '105'; # produit la chaîne '155' (si en ASCII)
$baz = 0+$foo & 0+$bar; # les deux opérandes explicitement numériques
$biz = "$foo" ^ "$bar"; # les deux opérandes explicitement chaînes
Voir vec pour savoir comment manipuler des bits individuellement dans un vecteur de bits.
Par défaut Perl suppose qu'il doit effectuer la plupart des calculs arithmétiques un virgule flottante. Mais en disant :
use integer;
vous dites au compilateur qu'il peut utiliser des opérations entières à partir de ce point et jusqu'à la fin du bloc englobant. Un BLOC interne peut contrer cette commande en disant :
no integer;
qui durera jusqu'à la fin de ce BLOC.
Le opérations bit à bit (``&'', ``|'', ``^'', ``~'', ``<<``, et ''>>``) produisent toujours des résultats entiers. (Mais voir
aussi Opérateurs bit à bit sur les chaînes.) Par contre, use integer a encore une influence sur eux. Par défaut, leurs résultats sont
interprétés comme des entiers non-signés. Par contre, si use integer est actif, leurs résultats sont interprétés comme des entiers signés. Par
exemple, ~0 est habituellement évalué comme un grande valeur entière. Par contre, use integer; ~0 donne -1 sur des machines à complément à deux.
Alors use integer propose une arithmétique uniquement entière, il n'y a aucun moyen similaire
d'imposer des arrondis ou des troncations à un certain nombre de décimales.
Pour arrondir à un nombre de décimales précis, la méthode la plus simple
est d'utiliser sprintf() ou printf().
Les nombres en virgule flottante ne sont qu'une approximation de ce que les mathématiciens appellent les nombres réels. Il y a infiniment plus de réels que de flottants donc il faut arrondir les angles. Par exemple :
printf "%.20g\n", 123456789123456789;
# produit 123456789123456784
Tester l'égalité ou l'inégalité exacte de nombres flottants n'est pas une bonne idée. Voici un moyen (relativement coûteux) pour tester l'égalité de deux nombres flottants sur un nombre particuliers de décimales. Voir le volume II de Knuth pour un traitement plus robuste de ce sujet.
sub fp_equal {
my ($X, $Y, $POINTS) = @_;
my ($tX, $tY);
$tX = sprintf("%.${POINTS}g", $X);
$tY = sprintf("%.${POINTS}g", $Y);
return $tX eq $tY;
}
Le module POSIX (qui fait partie de la distribution standard de perl)
implémente ceil(), floor() et un certain nombre
d'autres fonctions mathématiques et trigonométriques. Le module
Math::Complex (qui fait partie de la distribution standard de perl) définit
de nombreuses fonctions mathématiques qui peuvent aussi fonctionner sur des
nombres flottants. Math::Complex n'est pas aussi performant que POSIX mais
POSIX ne peut pas travailler avec des nombres complexes.
Arrondir dans une application financière peut avoir de sérieuses implications et la méthode d'arrondi utilisée doit être spécifiée précisément. Dans ce cas, il peut être plus sûr de ne pas faire confiance aux différents systèmes d'arrondi proposés par Perl mais plutôt d'implémenter vous-mêmes la fonction d'arrondi dont vous avez besoin.
Les modules standards Math::BigInt et Math::BigFloat fournissent des variables arithmétiques en précision infinie et redéfinissent les opérateurs correspondants. Au prix d'un peu d'espace et de beaucoup de temps, ils permettent d'éviter les embûches classiques associées aux représentations en précision limitée.
use Math::BigInt;
$x = Math::BigInt->new('123456789123456789');
print $x * $x;
# affiche +15241578780673678515622620750190521
Paul Gaborit <Paul.Gaborit@enstimac.fr>