perlform - Formats Perl
Perl possède un mécanisme qui permet de générer des rapports et
tableaux simples. Pour ce faire, il vous aide à écrire le code
de manière semblable à ce à quoi il ressemblera lors de l'impression.
On peut garder la trace du
nombre de lignes par page, sur quelle page on se trouve, quand
imprimer des entêtes, etc. Les mots-clés sont empruntés au FORTRAN :
format()
pour déclarer, write()
pour exécuter ; référez-vous à leurs
entrées dans la page de manuel perlfunc. Heureusement, le layout est largement plus
lisible, un peu comme l'instruction PRINT USING du BASIC. Voyez-le
comme une sorte de « nroff(1)
du pauvre ».
Les formats, comme les paquetages et les sous-programmes, sont déclarés plutôt qu'exécutés : ils peuvent donc apparaître à n'importe quel point de vos programmes, mais mieux vaut les regrouper. Ils ont leur propre espace de nommage, bien séparé des autres « types » de Perl. Cela signifie que peuvent coexister une fonction « bidule » et un format « bidule ». Cependant, le nom par défaut d'un format associé à un fichier est le nom du fichier. Par conséquent, le nom par défaut du format pour STDOUT est « STDOUT », et le nom du format par défaut pour le fichier TEMP est « TEMP ». Ils ont l'air semblables, mais ne le sont pas.
Les formats de sortie sont déclarés comme suit :
format NOM = LISTEDEFORMATS .
Si le nom est omis, le format «STDOUT» est alors automatiquement défini. LISTEDEFORMATS consiste en une suite de lignes, chacune d'elle pouvant être de l'un des trois types suivants :
Les lignes-images s'imprimeront exactement comme elles ont été écrites, à l'exception des champs remplacés par des arguments dans ces lignes. Chaque champ d'une ligne-image commence par une arrobe (@) ou un accent circonflexe (^). Ces lignes ne font l'objet d'aucune interprétation. Le champ @, à ne pas confondre avec le symbole de tableau « @ », est le type de champ normal. L'autre type, le champ ^, sert à faire du remplissage multiligne rudimentaire. On définit la longueur de champ en le remplissant avec les caractères « < », « > », ou « | », pour aligner, respectivement, à gauche, à droite, ou au centre. Si la variable excède la longueur spécifiée, elle sera tronquée.
On peut aussi aligner à droite en utilisant le caractère « # », avec un « . » optionnel, pour spécifier un champ numérique : l'alignement se fait sur le « . » décimal. Si la valeur spécifiée pour ces types de champs contient un retour à la ligne, seul le texte jusqu'au retour à la ligne est imprimé. Enfin, le champ spécial «@*» peut être employé pour écrire des valeurs multilignes non tronquées ; il doit apparaître seul sur la ligne.
Les valeurs sont spécifiées sur la ligne suivante, dans le même ordre que les champs images. Les expressions fournissant les valeurs doivent être séparées par des virgules. Les expressions sont toutes évaluées en tant que liste avant que la ligne ne soit traitée. Une seule expression peut donc créer toute une liste d'éléments. Les expressions peuvent être écrites sur plusieurs lignes, à condition de les placer entre parenthèses. En pareil cas, la parenthèse ouvrante doit commencer la première ligne. Si une expression se transforme en nombre avec une partie décimale, et si l'image correspondante spécifie que la partie décimale doit être affichée (c'est-à-dire, n'importe quelle image sauf des « # » sans un « . »), le caractère utilisé pour indiquer la coupure décimale sera toujours déterminé par la locale LC_NUMERIC utilisée. Cela signifie que si l'environnement dans lequel est lancé le script spécifie le français, une virgule sera affichée au lieu du point. Voyez la page de manuel perllocale et « MISE EN GARDE » pour les détails.
Les champs images commençant par ^ plutôt que @ subissent un
traitement spécial. Avec un champ #, le champ est vide si la valeur
n'est pas définie. Pour d'autres types de champs, le ^ permet de
choisir un type de remplissage. Au lieu d'avoir une expression
arbitraire, la valeur spécifiée doit être une variable scalaire
contenant une chaîne de caractères. Perl met autant de texte qu'il
peut dans le champ, puis supprime de la chaîne ce qu'il a déjà
affiché : à l'appel suivant de la même variable, il affichera la
suite des informations. Cela signifie que la variable elle-même est
modifiée pendant l'exécution du write(), et elle n'est pas retournée.
Vous devez normalement utiliser une séquence de champs alignés
verticalement pour afficher un bloc de texte. Vous pouvez terminer le
dernier champ par «...», qui apparaîtra si le texte est trop long
pour être affiché entièrement. Vous pouvez changer les caractères où
il est possible de changer de ligne en utilisant la variable $:
(c'est $FORMAT_LINE_BREAK_CHARACTERS si vous utilisez le module
English) pour satisfaire vos besoins.
L'utilisation des champs ^ peut produire des enregistrements de longueur variable. Si le texte à formater est court, vous pouvez supprimer les lignes vides en mettant un caractère « ~ » (tilde) n'importe où sur la ligne. Le tilde sera transformé en espace lors de l'affichage. Si vous mettez un deuxième tilde contigu au premier, la ligne sera répétée tant que les champs sur la ligne ne seront pas vides. (si vous utilisez un champ @, l'expression que vous lui avez donné a intérêt à ne pas donner la même valeur tout le temps !)
Les en-têtes de formulaires sont par défaut pris en charge par un format ayant le même nom que le fichier associé, avec «_TOP» ajouté. Il est affiché en haut de chaque page. Voyez write dans la page de manuel perlfunc.
Exemples :
# Un rapport sur le fichier /etc/passwd format STDOUT_TOP = Fichier de mots de passe Nom Login Bureau Uid Gid Maison ------------------------------------------------------------------ . format STDOUT = @<<<<<<<<<<<<<<<<<< @||||||| @<<<<<<@>>>> @>>>> @<<<<<<<<<<<<<<<<< $nom, $login, $bureau,$uid,$gid, $maison .
# Un formulaire de rapport de bogue format STDOUT_TOP = Bug Reports @<<<<<<<<<<<<<<<<<<<<<<< @||| @>>>>>>>>>>>>>>>>>>>>>>> $system, $%, $date ------------------------------------------------------------------ . format STDOUT = Sujet : @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $sujet Index : @<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< $index, $description Priorité : @<<<<<<<<< Date: @<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< $priorite, $date, $description De : @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< $de, $description Assigné à : @<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< $programmeur, $description ~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< $description ~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< $description ~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< $description ~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< $description ~ ^<<<<<<<<<<<<<<<<<<<<<<<... $description .
Vous pouvez mêler les print()
et les write()
sur le même canal de
sortie, mais vous devez prendre en charge $-
($FORMAT_LINES_LEFT
) vous-même.
Le nom du format en cours est enregistré dans la variable $~
($FORMAT_NAME
), le format d'en-tête en cours dans $^
($FORMAT_TOP_NAME
), le numéro de la page en cours dans $%
($FORMAT_PAGE_NUMBER
), et le nombre de lignes par page dans $=
($FORMAT_LINES_PER_PAGE
). Si filehandle doit être affiché
immédiatement, on l'indique par $|
($OUTPUT_AUTOFLUSH
). La
chaîne imprimée avant chaque début de page (sauf la première) est
enregistrée dans $^L
($FORMAT_FORMFEED
). Ces variables sont
spécifiques à un filehandle spécifique : vous devrez sélectionner
celui qui vous intéresse, avec select() :
select((select(OUTF), $~ = "Mon_Autre_Format", $^ = "Mon_Top_Format" )[0]);
C'est pas beau, hein ? C'est pourtant assez commun, donc, ne soyez pas trop surpris quand vous le verrez. Vous pouvez à la limite utiliser une variable temporaire pour pouvoir récupérer le filehandle précédent (c'est une bien meilleure approche en général, non seulement car vous avez plusieurs étapes pour pouvoir faire joujou avec le debuggeur) :
$ofh = select(OUTF); $~ = "Mon_Autre_Format"; $^ = "Mon_Top_Format"; select($ofh);
Si vous utilisez le module English, vous pouvez même lire les noms de variables :
use English; $ofh = select(OUTF); $FORMAT_NAME = "Mon_Autre_Format"; $FORMAT_TOP_NAME = "Mon_Top_Format"; select($ofh);
Mais il y a toujours les drôles de select(). Donc, utilisez juste le module FileHandle. Maintenant, vous pouvez accéder a ces variables spéciales en utilisant des méthodes en minuscules :
use FileHandle; format_name OUTF "Mon_autre_Format"; format_top_name OUTF "Mon_Top_Format";
Largement mieux !
Parce que les lignes contenant les valeurs peuvent contenir des
expressions arbitraires (pour les champs @, pas les ^), vous pouvez
construire des affichages très sophistiqués, comme sprintf()
ou une
bien à vous. Par exemple :
format Ident = @<<<<<<<<<<<<<<< &commify($n) .
Pour avoir un vrai @ ou ^ dans un champ, faites :
format Ident = J'ai un @ ici. "@" .
Pour centrer une ligne entière de texte, faites :
format Ident = @||||||||||||||||||||||||||||||||||||||||||||||| "Une ligne de texte" .
Il n'existe pas de façon prédéfinie de dire « mets ça à droite de la
page, quelle que soit sa largeur » : vous devez spécifier où chaque chose
doit aller. Dans les cas vraiment désespérés on peut générer un format
à la volée, en se basant sur le nombre de colonnes, pour ensuite
l'évaluer avec eval()
:
$format = "format STDOUT = \n" . '^' . '<' x $cols . "\n" . '$entry' . "\n" . "\t^" . "<" x ($cols-8) . "~~\n" . '$entry' . "\n" . ".\n"; print $format if $Debugging; eval $format; die $@ if $@;
qui générera un format ressemblant a quelque chose dans ce goût-là :
format STDOUT = ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $entry ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ $entry .
Voici un petit programme qui fait a peu près la même chose que fmt(1) :
format = ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~ $_
.
$/ = ''; while (<>) { s/\s*\n\s*/ /g; write; }
Alors que $FORMAT_TOP_NAME contient le nom de l'en-tête du format en cours, il n'y a pas de mécanisme de correspondance pour faire la même chose avec le pied de page. L'une des causes est qu'on ne connaît pas la taille d'un format avant qu'il ne soit évalué. C'est sur la liste des choses à faire.
Voici une première stratégie : si vous avez un pied de page de taille
constante, vous pouvez vérifier $FORMAT_LINES_LEFT avant chaque write()
et
imprimer le pied de page quand c'est nécessaire.
Une autre stratégie consiste à ouvrir un pipe sur soi-même, utilisant
open(MOIMEME, "|-")
(référez vous a open() dans la page de manuel perlfunc) et à ne faire que
des write()
sur MOIMEME plutôt que STDOUT. Faites en sorte que le processus
fils remanie STDIN pour ajouter le pied de page comme il vous plaît. Ce n'est
pas très pratique, mais c'est faisable.
Pour un accès de bas niveau au mécanisme de formatage, vous pouvez utiliser
formline()
et accéder à la variable $^A
(la variable $ACCUMULATOR)
directement.
Par exemple :
$str = formline <<'END', 1,2,3; @<<< @||| @>>> END
print "Yo, je viens de mettre `$^A' dans l'accumulateur !\n";
Ou faire une routine swrite(), qui est à write()
ce que sprintf()
est à
printf(), comme suit :
use Carp; sub swrite { croak "utilisation : swrite IMAGE ARGUMENTS" unless @_; my $format = shift; $^A = ""; formline($format,@_); return $^A; }
$string = swrite(<<'END', 1, 2, 3); Check me out @<<< @||| @>>> END print $string;
Le point isolé qui termine un format peut aussi provoquer la perte d'un courriel passant via un serveur de mail mal configuré (et l'expérience montre qu'une telle configuration est la règle, et non pas l'exception). Donc, lorsque vous envoyez un format par courriel, indentez-le ! ainsi, le point terminant le format ne soit pas aligné à gauche : cela évitera une coupe par le serveur SMTP.
Les variables lexicales (déclarées avec « my ») ne sont pas visibles dans un format sauf celui-ci est déclaré dans le champ de vision de la variable lexicale. Ils n'étaient pas visibles du tout avant la version 5.001.
Les formats sont le seul morceau de Perl qui utilisent de façon
inconditionnelle les
informations provenant de la locale d'un programme. Si l'environnement du
programme spécifie une locale LC_NUMERIC, elle sera toujours utilisée pour
spécifier le point décimal dans une sortie formatée. Perl ignore tout
simplement
le reste de la locale si le pragma use locale
n'est pas utilisé. Les sorties
formatées ne peuvent pas prendre en compte ce pragma car il est lié a la
structure de bloc du programme, et, pour des raison historiques, les formats
sont définis hors de cette structure. Référez-vous à la page de manuel perllocale pour plus
d'informations sur la gestion des locales.
Cette traduction française correspond à la version anglaise distribuée avec perl 5.005_02. Pour en savoir plus concernant ces traductions, consultez http://perl.enstimac.fr/.
Mathieu Arnold <arn_mat@club-internet.fr>.
Yves Maniette <yves@giga.sct.ub.es>, Gérard Delafond.