.\" Automatically generated by Pod::Man 2.06 (Pod::Simple 3.04) .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "PERLFAQ5 1" .TH PERLFAQ5 1 "2006-03-03" "DocFr" "User Contributed Perl Documentation" .SH "NAME/NOM" .IX Header "NAME/NOM" perlfaq5 \- Fichiers et formats ($Revision: 1.38 $, \f(CW$Date:\fR 1999/05/23 16:08:30 $) .SH "DESCRIPTION" .IX Header "DESCRIPTION" Cette section traite de E/S (Entre\*'es et Sorties) et autres e\*'le\*'ments connexes\ : descripteurs de fichiers, vidage de tampons, formats d'e\*'criture et pieds de page. .Sh "Comment vider/annuler les tampons en sortie\ ? Pourquoi m'en soucier\ ?" .IX Subsection "Comment vider/annuler les tampons en sortie? Pourquoi m'en soucier?" La librairie standard d'E/S du C (stdio) accumule normalement les caracte\*`res envoye\*'s aux divers pe\*'riphe\*'riques. Cela dans un but d'efficacite\*', pour e\*'viter d'effectuer un appel syste\*`me pour chaque octet. Pour chaque utilisation de \fIprint()\fR ou \fIwrite()\fR en Perl, cette accumulation a lieu dans des tampons. L'appel \fIsyswrite()\fR court-circuite stdio et e\*'limine donc toute accumulation dans l'espace utilisateur. .PP Dans la plupart des imple\*'mentations de stdio, le type d'accumulation en sortie et la taille des tampons varient suivant le pe\*'riphe\*'rique utilise\*'. Les disques utilisent un me\*'canisme de tampons organise\*'s en blocs, dont la taille est de 2k ou plus. Les tubes (pipes) et les prises (sockets) ont souvent des tampons dont la taille varie de 1/2 a\*` 2k. Les pe\*'riphe\*'riques se\*'rie (comme les modems ou les terminaux) ont une accumulation ligne a\*` ligne, et stdio n'envoie la ligne entie\*`re que lorsque le caracte\*`re de fin de ligne est rec\*,u. .PP Perl ne permet pas des sorties ve\*'ritablement non accumule\*'es (mis a\*` part ce que l'on peut obtenir par \f(CW\*(C`syswrite(OUT, $char, 1)\*(C'\fR). Ce qu'il permet est pluto\*^t une \*(L"accumulation par commande\*(R", ou\*` l'e\*'criture physique est effectue\*'e apre\*`s chaque commande d'e\*'criture. Vis\-a\*`\-vis du syste\*`me d'exploitation, c'est moins demandant que l'absence totale de tampon de sortie, tout en permettant aux donne\*'es de sortir lorsque vous le demandez. .PP Si vous vous attendez a\*` ce que vos caracte\*`res sortent sur votre pe\*'riphe\*'rique lorsque vous les y imprimez, il vous faudra activer le mode d'e\*'criture syste\*'matique (autoflush) des tampons attache\*'s a\*` son descripteur de fichier. Le contro\*^le se fait par le biais de \fIselect()\fR et de la variable \f(CW$|\fR (cf. perlvar/$ et \*(L"select\*(R" in perlfunc). .PP .Vb 3 \& $old_fh = select(OUTPUT_HANDLE); \& $| = 1; \& select($old_fh); .Ve .PP Ou, de fac\*,on plus idiomatique avec le traditionnel\ : .PP .Vb 1 \& select((select(OUTPUT_HANDLE), $| = 1)[0]); .Ve .PP Ou encore, si vous craignez la variable \f(CW$|\fR au point d'accepter de ralentir conside\*'rablement l'execution en chargeant plusieurs milliers de lignes de code de divers modules\ : .PP .Vb 3 \& use FileHandle; \& open(DEV, "+autoflush(1); .Ve .PP Ou avec les nouveaux modules IO::*\ : .PP .Vb 3 \& use IO::Handle; \& open(DEV, ">/dev/printer"); # mais ceci ? \& DEV\->autoflush(1); .Ve .PP Ou encore\ : .PP .Vb 5 \& use IO::Socket; # une prise avec des proprie\*'te\*'s de tube ? \& $sock = IO::Socket::INET\->new(PeerAddr => 'www.perl.com', \& PeerPort => 'http(80)', \& Proto => 'tcp'); \& die "$!" unless $sock; \& \& $sock\->autoflush(); \& print $sock "GET / HTTP/1.0" . "\e015\e012" x 2; \& $document = join('', <$sock>); \& print "DOC IS: $document\en"; .Ve .PP Veuillez noter que le retour chariot et la fin de ligne sont ca\*^ble\*'s en codage octal. C'est le \s-1SEUL\s0 moyen (pour l'instant) de s'assurer d'un vidage des tampons sur toutes les plates\-formes, y compris les Macintosh. Ainsi doit-il en e\*^tre pour la programmation re\*'seau\ : vous devriez vraiment pre\*'ciser le codage physique des terminaisons de ligne dans les protocoles re\*'seau conside\*'re\*'s. Dans la pratique \f(CW"\er\en"\fR convient souvent, mais ce n'est pas portable. .PP Cf. perlfaq9 pour d'autres exemples de re\*'cupe\*'ration d'\s-1URL\s0 sur le Web. .Sh "Comment changer une ligne/effacer une ligne/inse\*'rer une ligne au milieu/ajouter une ligne en te\*^te d'un fichier\ ?" .IX Subsection "Comment changer une ligne/effacer une ligne/inse'rer une ligne au milieu/ajouter une ligne en te^te d'un fichier?" Ce sont les ope\*'rations d'un e\*'diteur de texte. Perl n'est pas un e\*'diteur de texte. Perl est un langage de programmation. Vous devez de\*'composer le proble\*`me en appels de bas niveau pour lire, e\*'crire, ouvrir, fermer et chercher. .PP Bien que les humains aient tendance a\*` voir un fichier de texte comme une se\*'quence de lignes empile\*'es a\*` la manie\*`re d'un jeu de cartes \*(-- ou de cartes perfore\*'es \*(-- les ordinateurs voient pluto\*^t le fichier comme une se\*'quence d'octets. En ge\*'ne\*'ral, il n'y a pas de moyen pour Perl de se positionner simplement sur une ligne particulie\*`re dans un fichier, et d'y ajouter ou d'en retirer du texte a\*` cet endroit. .PP (Il y a des exceptions dans des cas bien spe\*'cifiques\ : Vous pouvez ajouter ou retirer des donne\*'es librement a\*` la fin du fichier. De meme pour le remplacement d'une suite d'octets par une autre suite de me\*^me longueur. On peut aussi utiliser des tableaux lie\*'s via \f(CW$DB_RECNO\fR comme de\*'crits dans DB_File. Une autre solution consiste a\*` manipuler des fichiers dont toutes les lignes sont d'e\*'gale longueur.) .PP La solution ge\*'ne\*'rale est de cre\*'er une copie temporaire du fichier avec les changements que vous de\*'sirer y apporter, puis d'e\*'craser l'original avec cette copie. En faisant abstraction des possibilite\*'s de verouillage\ : .PP .Vb 3 \& $old = $file; \& $new = "$file.tmp.$$"; \& $bak = "$file.orig"; \& \& open(OLD, "< $old") or die "can't open $old: $!"; \& open(NEW, "> $new") or die "can't open $new: $!"; \& \& # Correction des fautes de frappe, en pre\*'servant les majuscules \& while () { \& s/\eb(p)earl\eb/${1}erl/i; \& (print NEW $_) or die "can't write to $new: $!"; \& } \& \& close(OLD) or die "can't close $old: $!"; \& close(NEW) or die "can't close $new: $!"; \& \& rename($old, $bak) or die "can't rename $old to $bak: $!"; \& rename($new, $old) or die "can't rename $new to $old: $!"; .Ve .PP Perl peut effectuer ce genre de traitement automatiquement avec l'option \f(CW\*(C`\-i\*(C'\fR sur la ligne de commande, ou via sa cousine, la variable \f(CW$^I\fR (cf. perlrun pour plus de pre\*'cisions). Notez que \&\f(CW\*(C`\-i\*(C'\fR peut imposer de spe\*'cifier un suffixe sur certains syste\*`mes non-Unix\ ; lisez la documentation spe\*'cifique au portage de Perl sur votre plate\-forme. .PP .Vb 2 \& # Renume\*'rotation d'une suite de tests depuis la ligne de commande \& perl \-pi \-e 's/(^\es+test\es+)\ed+/ $1 . ++$count /e' t/op/taint.t \& \& # Depuis un script \& local($^I, @ARGV) = ('.orig', glob("*.c")); \& while (<>) { \& if ($. == 1) { \& print "This line should appear at the top of each file\en"; \& } \& s/\eb(p)earl\eb/${1}erl/i; # Efface les fautes, pas les majuscules \& print; \& close ARGV if eof; # Re\*'\-initialise $. \& } .Ve .PP Si vous avez besoin de vous positionner a\*` une ligne arbitraire dans un fichier qui change peu souvent, vous pouvez fabriquer un index des positions ou\*` chaque ligne se termine dans le fichier. Si le fichier est gros, un index de toutes les 10 ou 100 fins de lignes permettrait de se positionner puis de lire, de fac\*,on assez efficace. Si le fichier est trie\*', essayez la bibliothe\*`que look.pl (incluse dans la distribution standard de Perl). .PP Dans le cas bien spe\*'cifique de l'effacement de lignes a\*` la fin d'un fichier, vous pouvez vous rabattre sur \fItell()\fR et \fItruncate()\fR. L'extrait de code suivant efface la dernie\*`re ligne d'un fichier sans en faire de copie ou sans lire tout le fichier en me\*'moire\ : .PP .Vb 3 \& open (FH, "+< $file"); \& while ( ) { $addr = tell(FH) unless eof(FH) } \& truncate(FH, $addr); .Ve .PP Le traitement d'erreur est laisse\*' en exercice au lecteur. .Sh "Comment de\*'terminer le nombre de lignes d'un fichier\ ?" .IX Subsection "Comment de'terminer le nombre de lignes d'un fichier?" Un moyen assez efficace est de compter les caracte\*`res de fin de ligne dans le fichier. Le programme suivant utilise une proprie\*'te\*' de tr///, de\*'crite dans perlop. Si votre fichier de texte ne se termine pas par un caracte\*`re de fin de ligne, alors ce n'est pas vraiment un fichier de texte correct, et ce programme vous surprendra en indiquant une ligne de moins. .PP .Vb 6 \& $lines = 0; \& open(FILE, $filename) or die "Can't open `$filename\*': $!"; \& while (sysread FILE, $buffer, 4096) { \& $lines += ($buffer =~ tr/\en//); \& } \& close FILE; .Ve .PP On supposera qu'il n'y a aucune traduction parasite de caracte\*`re de fin de ligne a\*` de\*'plorer. .Sh "Comment cre\*'er un fichier temporaire\ ?" .IX Subsection "Comment cre'er un fichier temporaire?" Utiliser la me\*'thode de classe \f(CW\*(C`new_tmpfile\*(C'\fR du module IO::File pour obtenir un descripteur de fichier ouvert en lecture e\*'criture. A utiliser si le nom dudit fichier importe peu. .PP .Vb 3 \& use IO::File; \& $fh = IO::File\->new_tmpfile() \& or die "Unable to make new temporary file: $!"; .Ve .PP On peut aussi utiliser la fonction \f(CW\*(C`tmpnam\*(C'\fR du module \s-1POSIX\s0 pour obtenir un nom de fichier a\*` ouvrir soi\-me\*^me. A\*` utiliser lorsque l'on doit connai\*^tre le nom dudit fichier. .PP .Vb 2 \& use Fcntl; \& use POSIX qw(tmpnam); \& \& # essaie un nouveau nom jusqu'a\*` en obtenir un qui n'existe pas de\*'ja\*`... \& # ce test est superfe\*'tatoire, mais on n'est jamais trop prudent \& do { $name = tmpnam() } \& until sysopen(FH, $name, O_RDWR|O_CREAT|O_EXCL); \& \& # installe un gestionnaire de type atexit(), qui se chargera d'effacer \& # le fichier temporaire en cas de mort pre\*'mature\*'e. \& END { unlink($name) or die "Couldn't unlink $name: $!" } \& \& # maintenant, utilisons ce fichier temporaire... .Ve .PP Si vous tenez vraiment a\*` tout faire a\*` la main, utilisez l'\s-1ID\s0 du processus et/ou la valeur du compteur de temps. Si vous avez besoin de plusieurs fichiers temporaires, ayez recours a\*` un compteur\ : .PP .Vb 10 \& BEGIN { \& use Fcntl; \& my $temp_dir = \-d '/tmp' ? '/tmp' : $ENV{TMP} || $ENV{TEMP}; \& my $base_name = sprintf("%s/%d\-%d\-0000", $temp_dir, $$, time()); \& sub temp_file { \& local *FH; \& my $count = 0; \& until (defined(fileno(FH)) || $count++ > 100) { \& $base_name =~ s/\-(\ed+)$/"\-" . (1 + $1)/e; \& sysopen(FH, $base_name, O_WRONLY|O_EXCL|O_CREAT); \& } \& if (defined(fileno(FH)) \& return (*FH, $base_name); \& } else { \& return (); \& } \& } \& } .Ve .Sh "Comment manipuler un fichier avec des enregistrements de longueur fixe\ ?" .IX Subsection "Comment manipuler un fichier avec des enregistrements de longueur fixe?" Le plus efficace est d'utiliser \fIpack()\fR et \fIunpack()\fR. C'est plus rapide que d'utiliser \fIsubstr()\fR sur de nombreuses, nombreuses chai\*^nes. C'est plus lent pour seulement quelques unes. .PP Voici un morceau de code de\*'montrant comment de\*'compiler et recompiler ensuite des lignes formatte\*'es selon un sche\*'ma donne\*', ici la sortie du programme ps, version Berkeley\ : .PP .Vb 10 \& # exemple de ligne : \& # 15158 p5 T 0:00 perl /home/ram/bin/scripts/now\-what \& $PS_T = 'A6 A4 A7 A5 A*'; \& open(PS, "ps|"); \& print scalar ; \& while () { \& ($pid, $tt, $stat, $time, $command) = unpack($PS_T, $_); \& for $var (qw!pid tt stat time command!) { \& print "$var: <$$var>\en"; \& } \& print 'line=', pack($PS_T, $pid, $tt, $stat, $time, $command), \& "\en"; \& } .Ve .PP Nous avons utilise\*' \f(CW$$var\fR d'une fac\*,on de\*'fendue par \f(CW\*(C`use strict \&'refs'\*(C'\fR. En effet, nous promouvons une chai\*^ne au statut de re\*'fe\*'rence sur une variable scalaire par le biais d'une re\*'fe\*'rence symbolique. C'est justifie\*' dans des petits programmes, mais supporte mal l'utilisation intensive. D'autre part, cela ne fonctionne qu'avec des variables globales, par opposition aux lexicales. .Sh "Comment rendre un descripteur de fichier local a\*` une routine\ ? Comment passer ce descripteur a\*` d'autres routines\ ? Comment construire un tableau de descripteurs\ ?" .IX Subsection "Comment rendre un descripteur de fichier local a` une routine? Comment passer ce descripteur a` d'autres routines? Comment construire un tableau de descripteurs?" Le moyen le plus rapide, le plus simple, et le plus direct, consiste a\*` localiser le type universel (typeglob) du descripteur de fichier en question\ : .PP .Vb 1 \& local *TmpHandle; .Ve .PP Les types universels sont rapides (surtout compare\*'s a\*` leurs alternatives) et raisonnablement faciles a\*` utiliser, mais ils posse\*`dent un inconve\*'nient subtil. Si vous aviez, par exemple, une fonction appele\*'e \fITmpHandle()\fR, ou une variable nomme\*'e \f(CW%TmpHandle\fR, elle vient de vous e\*^tre masque\*'e. .PP .Vb 9 \& sub findme { \& local *HostFile; \& open(HostFile, ") { \& print if /\eb127\e.(0\e.0\e.)?1\eb/; \& } \& # *HostFile disparait et se ferme automatiquement ici \& } .Ve .PP Voici comment utiliser cela dans une boucle pour ouvrir et me\*'moriser un ensemble de descripteurs de fichiers. Nous utiliserons une paire ordonne\*'e place\*'e dans un tableau associatif afin de rendre aise\*' le tri du tableau selon l'ordre d'insertion. .PP .Vb 7 \& @names = qw(motd termcap passwd hosts); \& my $i = 0; \& foreach $filename (@names) { \& local *FH; \& open(FH, "/etc/$filename") || die "$filename: $!"; \& $file{$filename} = [ $i++, *FH ]; \& } \& \& # Utilisation des descripteurs du fichier stocke\*'s dans le tableau \& foreach $name (sort { $file{$a}[0] <=> $file{$b}[0] } keys %file) { \& my $fh = $file{$name}[1]; \& my $line = <$fh>; \& print "$name $. $line"; \& } .Ve .PP Pour passer des descripteurs de fichier a\*` des fonctions, le plus simple consiste a\*` les faire pre\*'ceder d'une e\*'toile, comme dans func(*STDIN). Voir \&\*(L"Comment passer/renvoyer {une fonction, un handle de fichier, un tableau, un hachage, une me\*'thode, une expression rationnelle}\ ?\*(R" in perlfaq7 pour plus de pre\*'cisions. .PP Si vous de\*'sirez cre\*'er de nombreux descripteurs anonymes, vous devriez regarder du co\*^te\*' des modules Symbol, FileHandle ou meme IO::Handle, etc... Voici un exemple de code e\*'quivalent utilisant Symbol::gensym, qui est raisonnablement peu cou\*^teux. .PP .Vb 6 \& foreach $filename (@names) { \& use Symbol; \& my $fh = gensym(); \& open($fh, "/etc/$filename") || die "open /etc/$filename: $!"; \& $file{$filename} = [ $i++, $fh ]; \& } .Ve .PP Ou aussi, en utilisant l'interface semi\-oriente\*'e objet du module FileHandle, qui n'est certainement pas peu cou\*^teux\ : .PP .Vb 1 \& use FileHandle; \& \& foreach $filename (@names) { \& my $fh = FileHandle\->new("/etc/$filename") or die "$filename: $!"; \& $file{$filename} = [ $i++, $fh ]; \& } .Ve .PP Comprenez bien que, quelle que soit l'origine du descripteur de fichier, sous forme de type universel (vraisemblablement localise\*') ou de descripteur anonyme obtenu par l'un des modules pre\*'cite\*'s, cela n'affecte en rien les re\*`gles pour le moins bizarres qui gouvernent la gestion des descripteurs indirects. Voir a\*` ce sujet la question suivante. .Sh "Comment utiliser un descripteur de fichier indirectement\ ?" .IX Subsection "Comment utiliser un descripteur de fichier indirectement?" Un descripteur de fichier indirect s'utilise par l'interme\*'diaire d'une variable place\*'e la\*` ou\*`, normalement, le langage s'attend a\*` trouver un descripteur de fichier. On obtient une telle variable ainsi\ : .PP .Vb 5 \& $fh = SOME_FH; # un mot brut est mal\-aime\*' de 'strict subs' \& $fh = "SOME_FH"; # mal\-aime\*' de 'strict refs'; me\*^me package seulement \& $fh = *SOME_FH; # type universel \& $fh = \e*SOME_FH; # re\*'ference sur un type universel (be\*'nissable) \& $fh = *SOME_FH{IO}; # IO::Handle be\*'ni du type universel *SOME_FH .Ve .PP Ou en utilisant la me\*'thode \f(CW\*(C`new\*(C'\fR des modules FileHandle ou \s-1IO\s0 pour cre\*'er un descripteur anonyme, et en affectant le re\*'sultat a\*` une variable scalaire, utilise\*'e ensuite comme si c'e\*'tait un descripteur de fichier normal\ : .PP .Vb 2 \& use FileHandle; \& $fh = FileHandle\->new(); \& \& use IO::Handle; # 5.004 ou mieux \& $fh = IO::Handle\->new(); .Ve .PP Vous pouvez alors utiliser ces objets comme un descripteur de fichier normal. Aux endroits ou\*` Perl s'attend a\*` trouver un descripteur de fichier, un descripteur indirect peut e\*^tre substitue\*'. Ce descripteur indirect est simplement une variable scalaire contenant un descripteur de fichier. Des fonctions comme \f(CW\*(C`print\*(C'\fR, \f(CW\*(C`open\*(C'\fR, \f(CW\*(C`seek\*(C'\fR, ou l'ope\*'rateur diamant \f(CW\*(C`\*(C'\fR acceptent soit un descripteur de fichier sous forme de nom, soit une variable scalaire contenant un descripteur\ : .PP .Vb 4 \& ($ifh, $ofh, $efh) = (*STDIN, *STDOUT, *STDERR); \& print $ofh "Type it: "; \& $got = <$ifh> \& print $efh "What was that: $got"; .Ve .PP Quand on veut passer un descripteur de fichier a\*` une fonction, il y a deux manie\*`res d'e\*'crire la routine\ : .PP .Vb 4 \& sub accept_fh { \& my $fh = shift; \& print $fh "Sending to indirect filehandle\en"; \& } .Ve .PP Ou on peut localiser un type universel (typeglob) et utiliser le nom de descripteur ainsi obtenu directement\ : .PP .Vb 4 \& sub accept_fh { \& local *FH = shift; \& print FH "Sending to localized filehandle\en"; \& } .Ve .PP Ces deux styles marchent aussi bien avec des objets, des types universels ou des descripteurs de fichiers re\*'els. (Ils pourraient aussi se contenter de chai\*^nes simples, dans certains cas, mais c'est pluto\*^t risque\*'.) .PP .Vb 2 \& accept_fh(*STDOUT); \& accept_fh($handle); .Ve .PP Dans les exemples ci\-dessus, nous avons affecte\*' le descripteur de fichier a\*` une variable scalaire avant de l'utiliser. La raison est que seules de simples variables scalaires, par opposition a\*` des expressions ou des notations indice\*'es dans des tableaux normaux ou associatifs, peuvent e\*^tre ainsi utilise\*'es avec des fonctions natives comme \f(CW\*(C`print\*(C'\fR, \f(CW\*(C`printf\*(C'\fR, ou l'ope\*'rateur diamant. Les exemples suivant sont invalides et ne passeront pas la phase de compilation\ : .PP .Vb 4 \& @fd = (*STDIN, *STDOUT, *STDERR); \& print $fd[1] "Type it: "; # INVALIDE \& $got = <$fd[0]> # INVALIDE \& print $fd[2] "What was that: $got"; # INVALIDE .Ve .PP Avec \f(CW\*(C`print\*(C'\fR et \f(CW\*(C`printf\*(C'\fR, on peut s'en sortir avec un bloc contenant une expression a\*` la place du descripteur de fichier normalement attendu\ : .PP .Vb 3 \& print { $fd[1] } "funny stuff\en"; \& printf { $fd[1] } "Pity the poor %x.\en", 3_735_928_559; \& # Pity the poor deadbeef. .Ve .PP Ce bloc est un bloc ordinaire, semblable a\*` tout autre, donc on peut y placer des expressions plus complexes. Ceci envoie le message vers une destination parmi deux\ : .PP .Vb 3 \& $ok = \-x "/bin/cat"; \& print { $ok ? $fd[1] : $fd[2] } "cat stat $ok\en"; \& print { $fd[ 1+ ($ok || 0) ] } "cat stat $ok\en"; .Ve .PP Cette fac\*,on de traiter \f(CW\*(C`print\*(C'\fR et \f(CW\*(C`printf\*(C'\fR comme si c'e\*'taient des appels a\*` des me\*'thodes objets ne fonctionne pas avec l'ope\*'rateur diamant. Et ce parce que c'est vraiment un ope\*'rateur et pas seulement une fonction avec un argument spe\*'cial, non de\*'limite\*' par une virgule. En supposant que l'on ait stocke\*' divers types universels dans une structure, comme montre\*' ci\-avant, on pourrait me\*^me utiliser la fonction native \f(CW\*(C`readline\*(C'\fR pour lire un enregistrement comme le fait \&\f(CW\*(C`<>\*(C'\fR. Avec l'initialisation montre\*'e ci-dessus pour \f(CW@fd\fR, cela marcherait, mais seulement parce que \fIreadline()\fR demande un type universel. Cela ne marcherait pas avec des objets ou des chai\*^nes, ce qui pourrait bien e\*^tre un de ces bugs non encore corrige\*'s. .PP .Vb 1 \& $got = readline($fd[0]); .Ve .PP Notons ici que cet exotisme des descripteurs indirects ne de\*'pend pas du fait qu'ils peuvent prendre la forme de chai\*^nes, types universels, objets, ou autres. C'est simplement du\*^ a\*` la syntaxe des ope\*'rateurs fondamentaux. Jouer a\*` l'oriente\*' objet ne serait d'aucune aide ici. .Sh "Comment mettre en place un pied-de-page avec \fIwrite()\fP\ ?" .IX Subsection "Comment mettre en place un pied-de-page avec write()?" Il n'y a pas de me\*'thode native pour accomplir cela, mais perlform indique une ou deux techniques qui permettent aux programmeurs intre\*'pides de s'en sortir. .Sh "Comment rediriger un \fIwrite()\fP dans une chai\*^ne\ ?" .IX Subsection "Comment rediriger un write() dans une chai^ne?" Voir \*(L"Acce\*`s aux formats de l'inte\*'rieur\*(R" in perlform pour son exemple de fonction \fIswrite()\fR. .Sh "Comment afficher mes nombres avec des virgules pour de\*'limiter les milliers\ ?" .IX Subsection "Comment afficher mes nombres avec des virgules pour de'limiter les milliers?" Voici une solution\ : .PP .Vb 5 \& sub commify { \& local $_ = shift; \& 1 while s/^([\-+]?\ed+)(\ed{3})/$1,$2/; \& return $_; \& } \& \& $n = 23659019423.2331; \& print "GOT: ", commify($n), "\en"; \& \& GOT: 23,659,019,423.2331 .Ve .PP Il n'est pas possible d'utiliser simplement\ : .PP .Vb 1 \& s/^([\-+]?\ed+)(\ed{3})/$1,$2/g; .Ve .PP puisqu'il faut recalculer les positions apre\*`s l'ajout de chaque virgule. .PP Cette autre solution ajoute des virgules sur tous les nombres contenus sur une ligne, qu'ils aient ou nom une partie de\*'cimale, qu'ils soient ou non pre\*'ce\*'de\*'s par un + ou un \-, ou autre\ : .PP .Vb 7 \& # Auteur: Andrew Johnson \& sub commify { \& my $input = shift; \& $input = reverse $input; \& $input =~ s<(\ed\ed\ed)(?=\ed)(?!\ed*\e.)><$1,>g; \& return scalar reverse $input; \& } .Ve .Sh "Comment traduire les tildes (~) dans un nom de fichier\ ?" .IX Subsection "Comment traduire les tildes (~) dans un nom de fichier?" Utiliser l'ope\*'rateur <> (appele\*' \fIglob()\fR), ainsi que de\*'crit dans perlfunc. Ceci requiert d'avoir un shell installe\*' qui comprenne les tildes, c'est\-a\*`\-dire csh ou tcsh ou (certaines versions de) ksh, et donc n'est pas force\*'ment portable. Le module Glob::KGlob (disponible sur \s-1CPAN\s0) imple\*'mente une fonctionnalite\*' de glob plus portable. .PP Depuis Perl, on peut utiliser ceci directement\ : .PP .Vb 11 \& $filename =~ s{ \& ^ ~ # cherche le tilde en te\*^te \& ( # sauvegarde dans $1 : \& [^/] # tout caracte\*`re sauf un slash \& * # et ce 0 ou plusieurs fois (0 pour mon propre login) \& ) \& }{ \& $1 \& ? (getpwnam($1))[7] \& : ( $ENV{HOME} || $ENV{LOGDIR} ) \& }ex; .Ve .Sh "Pourquoi les fichiers que j'ouvre en lecture\-e\*'criture ont-ils leur contenu efface\*' en premier lieu\ ?" .IX Subsection "Pourquoi les fichiers que j'ouvre en lecture-e'criture ont-ils leur contenu efface' en premier lieu?" Parce que vous faites quelque chose du genre indique\*' ci\-apre\*`s, qui tronque d'abord le fichier et \fIseulement ensuite\fR donnne un acce\*`s en lecture\-e\*'criture. .PP .Vb 1 \& open(FH, "+> /path/name"); # MAUVAIS (en ge\*'ne\*'ral) .Ve .PP Ai\*:e\ ! Il faudrait faire comme ceci, ce qui e\*'chouera si le fichier n'existe pas de\*'ja\*`. .PP .Vb 1 \& open(FH, "+< /path/name"); # ouvert pour mise a\*` jour .Ve .PP L'usage \*(L">\*(R" cre\*'e ou met toujours a\*` ze\*'ro. L'usage de \*(L"<\*(R" ne le fait jamais. Le \*(L"+\*(R" n'y change rien. .PP Voici diffe\*'rents exemples d'ouverture. Tous ceux qui utilisent \&\fIsysopen()\fR supposent que l'on a de\*'ja\*` fait\ : .PP .Vb 1 \& use Fcntl; .Ve .PP Pour ouvrir un fichier en lecture\ : .PP .Vb 2 \& open(FH, "< $path") || die $!; \& sysopen(FH, $path, O_RDONLY) || die $!; .Ve .PP Pour ouvrir un fichier en e\*'criture, cre\*'ant un nouveau fichier si ne\*'cessaire ou en tronquant le fichier existant sinon\ : .PP .Vb 3 \& open(FH, "> $path") || die $!; \& sysopen(FH, $path, O_WRONLY|O_TRUNC|O_CREAT) || die $!; \& sysopen(FH, $path, O_WRONLY|O_TRUNC|O_CREAT, 0666) || die $!; .Ve .PP Pour ouvrir un fichier en e\*'criture, cre\*'ant un fichier qui n'existe pas de\*'ja\*`\ : .PP .Vb 2 \& sysopen(FH, $path, O_WRONLY|O_EXCL|O_CREAT) || die $!; \& sysopen(FH, $path, O_WRONLY|O_EXCL|O_CREAT, 0666) || die $!; .Ve .PP Pour ouvrir un fichier avec ajout en fin, le cre\*'ant si ne\*'cessaire\ : .PP .Vb 3 \& open(FH, ">> $path") || die $!; \& sysopen(FH, $path, O_WRONLY|O_APPEND|O_CREAT) || die $!; \& sysopen(FH, $path, O_WRONLY|O_APPEND|O_CREAT, 0666) || die $!; .Ve .PP Pour ouvrir un fichier existant avec ajout en fin\ : .PP .Vb 1 \& sysopen(FH, $path, O_WRONLY|O_APPEND) || die $!; .Ve .PP Pour ouvrir un fichier existant en mode de mise a\*` jour\ : .PP .Vb 2 \& open(FH, "+< $path") || die $!; \& sysopen(FH, $path, O_RDWR) || die $!; .Ve .PP Pour ouvrir un fichier en mode de mise a\*` jour, avec cre\*'ation si besoin\ : .PP .Vb 2 \& sysopen(FH, $path, O_RDWR|O_CREAT) || die $!; \& sysopen(FH, $path, O_RDWR|O_CREAT, 0666) || die $!; .Ve .PP Pour ouvrir en mise a\*` jour un fichier qui n'existe pas de\*'ja\*`\ : .PP .Vb 2 \& sysopen(FH, $path, O_RDWR|O_EXCL|O_CREAT) || die $!; \& sysopen(FH, $path, O_RDWR|O_EXCL|O_CREAT, 0666) || die $!; .Ve .PP Enfin, pour ouvrir un fichier sans bloquer, avec cre\*'ation e\*'ventuelle\ : .PP .Vb 2 \& sysopen(FH, "/tmp/somefile", O_WRONLY|O_NDELAY|O_CREAT) \& or die "can't open /tmp/somefile: $!"; .Ve .PP Attention\ : ni la cre\*'ation, ni la destruction de fichier n'est garantie e\*^tre atomique a\*` travers \s-1NFS\s0. C'est\-a\*`\-dire que deux processus pourraient simultane\*'ment arriver a\*` cre\*'er ou a\*` effacer le me\*^me fichier sans erreur. En d'autres termes, O_EXCL n'est pas aussi exclusif que ce que l'on pourrait penser de prime abord. .PP Voir aussi la nouvelle page de documentation perlopentut si vous en disposez (a\*` partir de la version 5.6). .ie n .Sh "Pourquoi ai-je l'erreur ""Argument list too long"" avec <*> de temps en temps\ ?" .el .Sh "Pourquoi ai-je l'erreur ``Argument list too long'' avec <*> de temps en temps\ ?" .IX Subsection "Pourquoi ai-je l'erreur Argument list too long avec <*> de temps en temps?" L'ope\*'rateur \f(CW\*(C`<>\*(C'\fR est utilise\*' pour effectuer une globalisation (cf. ci\-dessus). Dans les versions de Perl pre\*'ce\*'dant la v5.6.0, l'ope\*'rateur \fIglob()\fR interne lance \fIcsh\fR\|(1) pour effectuer ladite comple\*'tion, mais csh ne peut pas traiter plus de 127 e\*'le\*'ments et retourne donc le message d'erreur \f(CW\*(C`Argument list too long\*(C'\fR. Ceux qui ont installe\*' tcsh a\*` la place de csh n'auront pas ce proble\*`me, mais les utilisateurs de leur code pourront en e\*^tre surpris. .PP Pour contourner cela, soit vous mettez Perl a\*` jour vers une version 5.6.0 ou supe\*'rieur, soit vous faites votre comple\*'tion vous\-me\*^me avec \&\fIreaddir()\fR et des motifs, soit vous utilisez un module comme Glob::KGlob, qui n'a pas recours au shell pour faire cette comple\*'tion. .Sh "Y a\-t-il une fuite / un bug avec \fIglob()\fP\ ?" .IX Subsection "Y a-t-il une fuite / un bug avec glob()?" De par son imple\*'mentation sur certains syste\*`mes d'exploitation, l'utilisation de la fonction \fIglob()\fR (directement ou sous sa forme \&\f(CW\*(C`<>\*(C'\fR dans un contexte scalaire), peut provoquer une fuite me\*'moire ou un comportement non\-pre\*'dictible. Il est donc pre\*'fe\*'rable de n'utiliser \fIglob()\fR que dans un contexte de liste. .ie n .Sh "Commend ouvrir un fichier dont le nom commence par "">"" ou avec des espaces en fin\ ?" .el .Sh "Commend ouvrir un fichier dont le nom commence par ``>'' ou avec des espaces en fin\ ?" .IX Subsection "Commend ouvrir un fichier dont le nom commence par > ou avec des espaces en fin?" En re\*`gle ge\*'ne\*'rale, perl ignore les espaces en fin de nom de fichier, et interpre\*`te certains caracte\*`res spe\*'ciaux en de\*'but de nom (ou un \*(L"|\*(R" final) pour de\*'clencher un traitement spe\*'cial. Pour empe\*^cher cela, voici un exemple de routine que l'on peut utiliser. Elle transforme des chemins incomplets en les ancrant explicitement dans l'arborescence du syste\*`me de fichiers et ajoute un caracte\*`re \s-1NUL\s0 a\*` la fin du nom pour s'assurer que perl utilisera ce nom tel quel\ : .PP .Vb 6 \& sub safe_filename { \& local $_ = shift; \& s#^([^./])#./$1#; \& $_ .= "\e0"; \& return $_; \& } \& \& $badpath = "<< $fn") or "couldn't open $badpath: $!"; .Ve .PP Ceci pre\*'sume que vous utilisez des chemins \s-1POSIX\s0 (portable operating systems interface). Si vous e\*^tes sous un syste\*`me ferme\*', non portable et proprie\*'taire, il se peut que vous deviez ajuster le \f(CW"./"\fR ci\-dessus. .PP Il serait toutefois bien plus clair d'utiliser \fIsysopen()\fR\ : .PP .Vb 4 \& use Fcntl; \& $badpath = "<<file.lock"")""\ ?" .el .Sh "Pourquoi ne pas faire simplement \f(CWopen(FH, ``>file.lock'')\fP\ ?" .IX Subsection "Pourquoi ne pas faire simplement open(FH, "">file.lock"")?" Un bout de code \fBA\*` \s-1NE\s0 \s-1PAS\s0 \s-1UTILISER\s0\fR est\ : .PP .Vb 2 \& sleep(3) while \-e "file.lock"; # MERCI DE NE PAS UTILISER \& open(LCK, "> file.lock"); # CE CODE FOIREUX .Ve .PP C'est un cas classique de conflit d'exe\*'cution (race condition)\ : on fait en deux temps quelque chose qui devrait e\*^tre re\*'alise\*' en une seule ope\*'ration. C'est pourquoi les microprocesseurs fournissent une instruction atomique appele\*'e test\-and\-set. En the\*'orie, ceci devrait fonctionner\ : .PP .Vb 2 \& sysopen(FH, "file.lock", O_WRONLY|O_EXCL|O_CREAT) \& or die "can't open file.lock: $!"; .Ve .PP sauf que, lamentablement, la cre\*'ation (ou l'effacement) n'est pas atomique a\*` travers \s-1NFS\s0, donc cela ne marche pas (du moins, pas tout le temps) a\*` travers le re\*'seau. De multiples sche\*'mas utilisant \fIlink()\fR ont e\*'te\*' sugge\*'re\*'s, mais ils ont tous tendance a\*` mettre en jeu une boucle d'attente active, ce qui est tout autant inde\*'sirable. .Sh "Je ne comprends toujours pas le verrouillage. Je veux seulement incre\*'menter un compteur dans un ficher. Comment faire\ ?" .IX Subsection "Je ne comprends toujours pas le verrouillage. Je veux seulement incre'menter un compteur dans un ficher. Comment faire?" On ne vous a pas assez dit que les compteurs d'acce\*`s aux pages web e\*'taient inutiles\ ? Ils ne comptent pas vraiment le nombre d'acce\*`s, sont une perte de temps, et ne servent qu'a\*` gonfler la vanite\*' de leur auteur. Il vaudrait mieux choisir un nombre au hasard. Ce serait plus re\*'aliste. .PP Quoiqu'il en soit, voici ce que vous pouvez faire si vous ne pouvez pas vous retenir\ : .PP .Vb 11 \& use Fcntl ':flock'; \& sysopen(FH, "numfile", O_RDWR|O_CREAT) or die "can't open numfile: $!"; \& flock(FH, LOCK_EX) or die "can't flock numfile: $!"; \& $num = || 0; \& seek(FH, 0, 0) or die "can't rewind numfile: $!"; \& truncate(FH, 0) or die "can't truncate numfile: $!"; \& (print FH $num+1, "\en") or die "can't write numfile: $!"; \& # A\*` partir de la version 5.004, Perl vide automatiquement les \& # buffers avant de de\*'verrouiller \& flock(FH, LOCK_UN) or die "can't flock numfile: $!"; \& close FH or die "can't close numfile: $!"; .Ve .PP Voici un bien meilleur compteur d'acce\*`s aux pages web\ : .PP .Vb 1 \& $hits = int( (time() \- 850_000_000) / rand(1_000) ); .Ve .PP Si le compteur n'impressionne pas vos amis, le code, lui, pourrait...\ :\-) .Sh "Comment modifier un fichier binaire directement\ ?" .IX Subsection "Comment modifier un fichier binaire directement?" Si vous essayez simplement de fixer un binaire, ce bout de code, pour simple qu'il soit, fonctionne bien\ : .PP .Vb 1 \& perl \-i \-pe 's{window manager}{window mangler}g' /usr/bin/emacs .Ve .PP Cependant, si vous avez des enregistrements de taille fixe, alors vous pourriez faire pluto\*^t comme ceci\ : .PP .Vb 9 \& $RECSIZE = 220; # taille en octets de l'enregistrement \& $recno = 37; # nume\*'ro d'enregistrement a\*` modifier \& open(FH, "+mtime); \& print "file $file updated at $date_string\en"; .Ve .PP L'approche \fIPOSIX::strftime()\fR a le be\*'ne\*'fice d'e\*^tre, en the\*'orie, inde\*'pendante de la localisation courante. Voir perllocale pour plus de de\*'tails. .Sh "Comment imprimer une estampille temporelle sur un fichier en perl\ ?" .IX Subsection "Comment imprimer une estampille temporelle sur un fichier en perl?" Utiliser le fonction \fIutime()\fR documente\*'e dans \*(L"utime\*(R" in perlfunc. A\*` titre d'exemple, voici un petit programme qui applique les estampilles temporelles de lecture et d'e\*'criture de son premier argument sur tous les autres\ : .PP .Vb 6 \& if (@ARGV < 2) { \& die "usage: cptimes timestamp_file other_files ...\en"; \& } \& $timestamp = shift; \& ($atime, $mtime) = (stat($timestamp))[8,9]; \& utime $atime, $mtime, @ARGV; .Ve .PP Le traitement d'erreurs est laisse\*', comme d'habitude, en exercice au lecteur. .PP Notez que \fIutime()\fR ne marche pas correctement pour l'instant sur Win95/NT. Un bug a e\*'te\*' signale\*'. Verifiez soigneusement avant de l'utiliser sur ces plateformes. .Sh "Comment e\*'crire dans plus d'un fichier en me\*^me temps\ ?" .IX Subsection "Comment e'crire dans plus d'un fichier en me^me temps?" Pour une utilisation unique, on peut recourir a\*`\ : .PP .Vb 1 \& for $fh (FH1, FH2, FH3) { print $fh "whatever\en" } .Ve .PP Pour connecter un descripteur de fichier a\*` plusieurs descripteurs en sortie, il est plus aise\*' d'utiliser le programme \fItee\fR\|(1) si vous l'avez, et de le laisser se charger du multiplexage. .PP .Vb 1 \& open (FH, "| tee file1 file2 file3"); .Ve .PP Ou me\*^me\ : .PP .Vb 4 \& # STDOUT redirige\*' vers trois fichiers \& open (STDOUT, "| tee file1 file2 file3") or die "Teeing off: $!\en"; \& print "whatever\en" or die "Writing: $!\en"; \& close(STDOUT) or die "Closing: $!\en"; .Ve .PP Sinon, il vous faudra e\*'crire votre propre fonction de multiplexage \*(-- ou votre propre programme tee \*(-- ou utiliser celui de Tom Christiansen disponible sur http://www.perl.com/CPAN/authors/id/TOMC/scripts/tct.gz, qui est e\*'crit en Perl et qui offre de plus nombreuses fonctionnalite\*'s que l'original. .Sh "Comment lire tout un fichier d'un seul coup\ ?" .IX Subsection "Comment lire tout un fichier d'un seul coup?" L'approche habituelle en Perl pour traiter toutes les lignes d'un fichier est de le faire une ligne a\*` la fois\ : .PP .Vb 6 \& open (INPUT, $file) || die "can't open $file: $!"; \& while () { \& chomp; \& # do something with $_ \& } \& close(INPUT) || die "can't close $file: $!"; .Ve .PP Ceci est terriblement plus efficace que de lire le fichier tout entier en me\*'moire en tant que tableau de ligne puis de le traiter un e\*'le\*'ment a\*` la fois, ce qui est souvent \*(-- sinon presque toujours \*(-- la mauvaise approche. Chaque fois que vous voyez quelqu'un faire ceci\ : .PP .Vb 1 \& @lines = ; .Ve .PP Vous devriez re\*'fle\*'chir longuement et profonde\*'ment a\*` la raison pour laquelle vous auriez besoin que tout soit charge\*' en me\*^me temps. Ce n'est tout simplement pas une solution extensible. Vous pourriez aussi trouver plus amusant d'utiliser les liens \f(CW$DB_RECNO\fR du module standard DB_File, qui vous permettent de lier un tableau a\*` un fichier de fac\*,on qu'acce\*'der a\*` un e\*'le\*'ment du tableau acce\*`de en ve\*'rite\*' a\*` la ligne correspondante dans le fichier. .PP En de tre\*`s rares occasions, vous pouvez rencontrer un algorithme qui exige que la totalite\*' du fichier soit en me\*'moire a\*` la fois en tant que scalaire. La solution la plus simple est\ : .PP .Vb 1 \& $var = `cat $file`; .Ve .PP E\*'tant dans un contexte scalaire, vous obtenez toute la chose. Dans un contexte de liste, vous obtiendriez une liste de toutes les lignes\ : .PP .Vb 1 \& @lines = `cat $file`; .Ve .PP Cette solution petite mais expe\*'ditive est mignonne, propre, et portable sur tous les syste\*`mes sur lesquels des outils de\*'cents ont e\*'te\*' installe\*'s. Pour ceux qui pre\*'fe\*`rent se passer de la boi\*^te a\*` outils, vous pouvez bien su\*^r lire le fichier manuellement, bien que ceci produise un code plus complique\*'. .PP .Vb 5 \& { \& local(*INPUT, $/); \& open (INPUT, $file) || die "can't open $file: $!"; \& $var = ; \& } .Ve .PP Ceci inde\*'finit temporairement votre se\*'parateur d'enregistrements, et fermera automatiquement le fichier a\*` la sortie du bloc. Si le fichier est de\*'ja\*` ouvert, utilisez juste ceci\ : .PP .Vb 1 \& $var = do { local $/; }; .Ve .Sh "Comment lire un fichier paragraphe par paragraphe\ ?" .IX Subsection "Comment lire un fichier paragraphe par paragraphe?" Utilisez la variable \f(CW$/\fR (voir perlvar pour plus de de\*'tails). Vous pouvez soit la positionner a\*` \f(CW""\fR pour e\*'liminer les paragraphes vides (\f(CW"abc\en\en\en\endef"\fR, par exemple, sera traite\*' comme deux paragraphes et non trois), soit a\*` \f(CW"\en\en"\fR pour accepter les paragraphes vides. .PP Notez qu'une ligne blanche ne doit pas contenir de blancs. Ainsi, \f(CW"fred\en \&\enstuff\en\en"\fR est un paragraphe, mais \f(CW"fred\en\enstuff\en\en"\fR en fait deux. .Sh "Comment lire un seul caracte\*`re d'un fichier\ ? Et du clavier\ ?" .IX Subsection "Comment lire un seul caracte`re d'un fichier? Et du clavier?" Vous pouvez utiliser la fonction native \f(CW\*(C`getc()\*(C'\fR sur la plupart des descripteurs de fichier, mais elle ne marchera pas (facilement) sur un terminal. Pour \s-1STDIN\s0, utilisez soit le module Term::ReadKey disponible sur \s-1CPAN\s0, ou l'exemple fourni dans \*(L"getc\*(R" in perlfunc. .PP Si votre syste\*`me supporte \s-1POSIX\s0 (portable operating system programming interface), vous pouvez utiliser le code suivant, qui, vous l'aurez note\*', supprime aussi l'echo pendant le traitement. .PP .Vb 10 \& #!/usr/bin/perl \-w \& use strict; \& $| = 1; \& for (1..4) { \& my $got; \& print "gimme: "; \& $got = getone(); \& print "\-\-> $got\en"; \& } \& exit; \& \& BEGIN { \& use POSIX qw(:termios_h); \& \& my ($term, $oterm, $echo, $noecho, $fd_stdin); \& \& $fd_stdin = fileno(STDIN); \& \& $term = POSIX::Termios\->new(); \& $term\->getattr($fd_stdin); \& $oterm = $term\->getlflag(); \& \& $echo = ECHO | ECHOK | ICANON; \& $noecho = $oterm & ~$echo; \& \& sub cbreak { \& $term\->setlflag($noecho); \& $term\->setcc(VTIME, 1); \& $term\->setattr($fd_stdin, TCSANOW); \& } \& \& sub cooked { \& $term\->setlflag($oterm); \& $term\->setcc(VTIME, 0); \& $term\->setattr($fd_stdin, TCSANOW); \& } \& \& sub getone { \& my $key = ''; \& cbreak(); \& sysread(STDIN, $key, 1); \& cooked(); \& return $key; \& } \& \& } \& \& END { cooked() } .Ve .PP Le module Term::ReadKey de \s-1CPAN\s0 est sans doute plus facile a\*` utiliser. Les versions re\*'centes incluent aussi un support pour les syste\*`mes non portables. .PP .Vb 8 \& use Term::ReadKey; \& open(TTY, " nous a dit\ : .PP Pour mettre les \s-1PC\s0 en mode \*(L"brut\*(R", utiliser \fIioctl()\fR avec des valeurs magiques glanne\*'es dans msdos.c (sources de Perl) et dans la liste des interruptions de Ralf Brown (qui circule sur Internet de temps en temps)\ : .PP .Vb 3 \& $old_ioctl = ioctl(STDIN,0,0); # Lis les infos sur le terminal \& $old_ioctl &= 0xff; \& ioctl(STDIN,1,$old_ioctl | 32); # Positionne le bit 5 .Ve .PP Puis pour lire un simple caracte\*`re\ : .PP .Vb 1 \& sysread(STDIN,$c,1); # Lis un caracte\*`re .Ve .PP Pour replacer le \s-1PC\s0 en mode \*(L"normal\*(R"\ : .PP .Vb 1 \& ioctl(STDIN,1,$old_ioctl); # Retourne en mode normal .Ve .PP Donc a\*` pre\*'sent, vous avez \f(CW$c\fR. Si \f(CW\*(C`ord($c) == 0\*(C'\fR, c'est un code sur deux octets, donc vous avez tape\*' une touche spe\*'ciale. Relisez un autre octet avec \f(CW\*(C`sysread(STDIN,$c,1)\*(C'\fR, et cette valeur indique la touche via cette table de correspondance\ : .PP .Vb 1 \& # PC 2\-byte keycodes = ^@ + the following: \& \& # HEX KEYS \& # \-\-\- \-\-\-\- \& # 0F SHF TAB \& # 10\-19 ALT QWERTYUIOP \& # 1E\-26 ALT ASDFGHJKL \& # 2C\-32 ALT ZXCVBNM \& # 3B\-44 F1\-F10 \& # 47\-49 HOME,UP,PgUp \& # 4B LEFT \& # 4D RIGHT \& # 4F\-53 END,DOWN,PgDn,Ins,Del \& # 54\-5D SHF F1\-F10 \& # 5E\-67 CTR F1\-F10 \& # 68\-71 ALT F1\-F10 \& # 73\-77 CTR LEFT,RIGHT,END,PgDn,HOME \& # 78\-83 ALT 1234567890\-= \& # 84 CTR PgUp .Ve .PP C'est tout les essais que j'ai effectue\*'s il y a longtemps. J'espe\*`re que je suis en train de lire le fichier qui marchait bien. .Sh "Comment savoir si un caracte\*`re est disponible sur un descripteur\ ?" .IX Subsection "Comment savoir si un caracte`re est disponible sur un descripteur?" La premie\*`re chose a\*` faire, c'est de se procurer le module Term::ReadKey depuis \s-1CPAN\s0. Comme nous le mentionnions plus haut, il contient me\*^me de\*'sormais un support limite\*' pour les syste\*`mes non portables (comprendre\ : syste\*`mes non ouverts, ferme\*'s, proprie\*'taires, non \s-1POSIX\s0, non Unix, etc.). .PP Vous devriez aussi lire la Foire Aux Questions de comp.unix.* pour ce genre de chose\ : la re\*'ponse est sensiblement identique. C'est tre\*`s de\*'pendant du syste\*`me d'exploitation utilise\*'. Voici une solution qui marche sur les syste\*`mes \s-1BSD\s0\ : .PP .Vb 5 \& sub key_ready { \& my($rin, $nfd); \& vec($rin, fileno(STDIN), 1) = 1; \& return $nfd = select($rin,undef,undef,0); \& } .Ve .PP Si vous de\*'sirez savoir combien de caracte\*`res attendent, regardez du co\*^te\*' de \fIioctl()\fR et de \s-1FIONREAD\s0. L'outil \fIh2ph\fR qui est fourni avec Perl essaie de convertir les fichiers d'inclusion du C en code Perl, qui peut alors e\*^tre utilise\*' via \f(CW\*(C`require\*(C'\fR. \s-1FIONREAD\s0 se retrouve de\*'finit comme une fonction dans le fichier \fIsys/ioctl.ph\fR\ : .PP .Vb 1 \& require 'sys/ioctl.ph'; \& \& $size = pack("L", 0); \& ioctl(FH, FIONREAD(), $size) or die "Couldn't call ioctl: $!\en"; \& $size = unpack("L", $size); .Ve .PP Si \fIh2ph\fR n'a pas e\*'te\*' installe\*', ou s'il ne fonctionne pas pour vous, il est possible d'utiliser \fIgrep\fR sur les fichiers directement\ : .PP .Vb 2 \& % grep FIONREAD /usr/include/*/* \& /usr/include/asm/ioctls.h:#define FIONREAD 0x541B .Ve .PP Ou e\*'crivez un petit programme C, en utilisant l'e\*'diteur des champions\ : .PP .Vb 9 \& % cat > fionread.c \& #include \& main() { \& printf("%#08x\en", FIONREAD); \& } \& ^D \& % cc \-o fionread fionread.c \& % ./fionread \& 0x4004667f .Ve .PP Puis, ca\*^blez la valeur, en laissant les proble\*`mes de portage comme exercice a\*` votre successeur. .PP .Vb 1 \& $FIONREAD = 0x4004667f; # XXX: depend du syste\*`me d'exploitation \& \& $size = pack("L", 0); \& ioctl(FH, $FIONREAD, $size) or die "Couldn't call ioctl: $!\en"; \& $size = unpack("L", $size); .Ve .PP \&\s-1FIONREAD\s0 impose un descripteur connecte\*' a\*` un canal (stream), ce qui signifie qu'il fonctionne bien avec les prises (sockets), tubes (pipes) et terminaux (tty), mais \fIpas\fR avec les fichiers. .ie n .Sh "Comment e\*'crire un ""tail \-f"" en perl\ ?" .el .Sh "Comment e\*'crire un \f(CWtail \-f\fP en perl\ ?" .IX Subsection "Comment e'crire un tail -f en perl?" Essayez d'abord\ : .PP .Vb 1 \& seek(GWFILE, 0, 1); .Ve .PP La ligne \f(CW\*(C`seek(GWFILE, 0, 1)\*(C'\fR ne change pas la postion courante, mais elle efface toute indication de fin de fichier sur le descripteur, de sorte que le prochain <\s-1GWFILE\s0> conduira Perl a\*` essayer de nouveau de lire quelque chose. .PP Si cela ne fonctionne pas (cela demande certaines proprie\*'te\*'s a\*` votre stdio), alors vous pouvez essayer quelque chose comme\ : .PP .Vb 7 \& for (;;) { \& for ($curpos = tell(GWFILE); ; $curpos = tell(GWFILE)) { \& # cherche des trucs, et mets\-les quelque part \& } \& # attendre un peu \& seek(GWFILE, $curpos, 0); # retourne ou\*` nous en e\*'tions \& } .Ve .PP Si cela ne marche pas non plus, regardez du co\*^te\*' du module \s-1POSIX\s0. \&\s-1POSIX\s0 de\*'finit la fonction \fIclearerr()\fR, qui peut o\*^ter la condition de fin de fichier sur le descripteur. La me\*'thode\ : lire jusqu'a\*` obtenir une fin de fichier, \fIclearerr()\fR, lire la suite. Nettoyer, rincer, et ainsi de suite. .PP Il existe aussi un module File::Tail sur le \s-1CPAN\s0. .Sh "Comment faire un \fIdup()\fP sur un descripteur en Perl\ ?" .IX Subsection "Comment faire un dup() sur un descripteur en Perl?" Voir \*(L"open\*(R" in perlfunc pour obtenir divers moyens d'appeler \fIopen()\fR qui pourraient vous convenir. Par exemple\ : .PP .Vb 2 \& open(LOG, ">>/tmp/logfile"); \& open(STDERR, ">&LOG"); .Ve .PP Ou me\*^me avec des descripteurs nume\*'riques\ : .PP .Vb 2 \& $fd = $ENV{MHCONTEXTFD}; \& open(MHCONTEXT, "<&=$fd"); # comme fdopen(3S) .Ve .PP Noter que \*(L"<&STDIN\*(R" donne un clone, mais que \*(L"<&=STDIN\*(R" donne un alias. Cela veut dire que si vous fermez un descripteur posse\*'dant des alias, ceux-ci deviennent indisponibles. C'est faux pour un clone. .PP Comme d'habitude, le traitement d'erreur est laisse\*' en exercice au lecteur. .Sh "Comment fermer un descripteur connu par son nume\*'ro\ ?" .IX Subsection "Comment fermer un descripteur connu par son nume'ro?" Cela devrait n'e\*^tre que tre\*`s rarement ne\*'cessaire, puisque la fonction \&\fIclose()\fR de Perl n'est cense\*'e e\*^tre utilise\*'e que pour des choses ouvertes par Perl, me\*^me si c'est un clone (dup) de descripteur nume\*'rique comme \s-1MHCONTEXT\s0 ci\-dessus. Mais si vraiment requis, vous pourriez essayer\ : .PP .Vb 3 \& require 'sys/syscall.ph'; \& $rc = syscall(&SYS_close, $fd + 0); # doit forcer une valeur nume\*'rique \& die "can't sysclose $fd: $!" unless $rc == \-1; .Ve .PP Ou bien utilisez juste la caracte\*'ristique fdopen(3S) de \fIopen()\fR\ : .PP .Vb 5 \& { \& local *F; \& open F, "<&=$fd" or die "Cannot reopen fd=$fd: $!"; \& close F; \& } .Ve .ie n .Sh "Pourquoi ""C:\etemp\efoo"" n'indique pas un fichier \s-1DOS\s0\ ? Et me\*^me ""C:\etemp\efoo.exe"" ne marche pas\ ?" .el .Sh "Pourquoi ``C:\etemp\efoo'' n'indique pas un fichier \s-1DOS\s0\ ? Et me\*^me ``C:\etemp\efoo.exe'' ne marche pas\ ?" .IX Subsection "Pourquoi C:tempfoo n'indique pas un fichier DOS? Et me^me C:tempfoo.exe ne marche pas?" Ouille\ ! Vous venez de mettre un tab et un formfeed dans le nom du fichier. Rappelez-vous qu'a\*` l'inte\*'rieur de doubles guillemets (\*(L"\etel quel\*(R"), le backslash est un caracte\*`re d'e\*'chappement. La liste comple\*`te de telles se\*'quences est dans \*(L"Ope\*'rateurs apostrophe et type apostrophe\*(R" in perlop. Evidemment, vous n'avez pas de fichier appele\*' \*(L"c:(tab)emp(formfeed)oo\*(R" ou \&\*(L"c:(tab)emp(formfeed)oo.exe\*(R" sur votre syste\*`me de fichiers \s-1DOS\s0 originel. .PP Utilisez soit des guillemets simples (apostrophes) pour de\*'limiter vos chai\*^nes, ou (mieux) utilisez des slashes. Toutes les versions de \s-1DOS\s0 et de Windows venant apre\*`s MS-DOS 2.0 traitent \f(CW\*(C`/\*(C'\fR et \f(CW\*(C`\e\*(C'\fR de la me\*^me fac\*,on dans les noms de fichier, donc autant utiliser une forme compatible avec Perl \*(-- ainsi qu'avec le shell \s-1POSIX\s0, \s-1ANSI\s0 C et \*(C+, awk, Tcl, Java, ou Python, pour n'en mentionner que quelques autres. Les chemins \s-1POSIX\s0 sont aussi plus portables. .ie n .Sh "Pourquoi glob(""*.*"") ne donne-t-il pas tous les fichiers\ ?" .el .Sh "Pourquoi glob(``*.*'') ne donne-t-il pas tous les fichiers\ ?" .IX Subsection "Pourquoi glob(*.*) ne donne-t-il pas tous les fichiers?" Parce que, me\*^me sur les portages non\-Unix, la fonction \fIglob()\fR de Perl suit la se\*'mantique normale de comple\*'tion d'Unix. Il faut utiliser \&\f(CW\*(C`glob("*")\*(C'\fR pour obternir tous les fichiers (non cache\*'s). Cela contribue a\*` rendre \fIglob()\fR portable y compris sur les syste\*`mes ancestraux. Votre portage peut aussi inclure des fonctions de globalisation proprie\*'taires. Regardez sa documentation pour plus de de\*'tails. .ie n .Sh "Pourquoi Perl me laisse effacer des fichiers prote\*'ge\*'s en e\*'criture\ ? Pourquoi ""\-i"" e\*'crit\-il dans des fichiers prote\*'ge\*'s\ ? N'est\-ce pas un bug de Perl\ ?" .el .Sh "Pourquoi Perl me laisse effacer des fichiers prote\*'ge\*'s en e\*'criture\ ? Pourquoi \f(CW\-i\fP e\*'crit\-il dans des fichiers prote\*'ge\*'s\ ? N'est\-ce pas un bug de Perl\ ?" .IX Subsection "Pourquoi Perl me laisse effacer des fichiers prote'ge's en e'criture? Pourquoi -i e'crit-il dans des fichiers prote'ge's? N'est-ce pas un bug de Perl?" Ce sujet est traite\*' de fac\*,on comple\*`te et fastidieuse dans le \*(L"Far More Than You Ever Wanted To Know\*(R" disponible sur http://www.perl.com/CPAN/doc/FMTEYEWTK/file\-dir\-perms . .PP Pour re\*'sumer, apprenez comment fonctionne votre syste\*`me de fichiers. Les permissions sur un fichier indiquent seulement ce qui peut arriver aux donne\*'es dudit fichier. Les permissions sur le re\*'pertoire indiquent ce qui peut survenir a\*` la liste des fichiers contenus dans ce re\*'pertoire. Effacer un fichier revient a\*` l'o\*^ter de la liste du re\*'pertoire (donc l'ope\*'ration est re\*'gie par les permissions sur le re\*'pertoire, pas sur le fichier). Si vous essayez d'e\*'crire dans le fichier, alors les permissions du fichiers sont prises en compte pour de\*'terminer si vous en avez le droit. .Sh "Comment se\*'lectionner une ligne au hasard dans un fichier\ ?" .IX Subsection "Comment se'lectionner une ligne au hasard dans un fichier?" Voici un algorithme tire\*' du Camel Book\ : .PP .Vb 2 \& srand; \& rand($.) < 1 && ($line = $_) while <>; .Ve .PP Il a un e\*'norme avantage en espace par rapport a\*` la solution consistant a\*` tout lire en me\*'moire. Une preuve simple par induction de son exactitude est disponible sur reque\*^te, au cas ou\*` vous en douteriez. .Sh "Pourquoi obtient-on des espaces e\*'tranges lorsqu'on affiche un tableau de lignes\ ?" .IX Subsection "Pourquoi obtient-on des espaces e'tranges lorsqu'on affiche un tableau de lignes?" Le fait de dire .PP .Vb 1 \& print "@lines\en"; .Ve .PP joint les e\*'le\*'ments de \f(CW@lines\fR avec une espace entre eux. Si \&\f(CW@lines\fR valait \f(CW\*(C`("little", "fluffy", "clouds")\*(C'\fR, alors l'instruction pre\*'ce\*'dente afficherait\ : .PP .Vb 1 \& little fluffy clouds .Ve .PP mais si chaque e\*'le\*'ment de \f(CW@lines\fR e\*'tait une ligne de texte, termine\*'e par une fin de ligne, \f(CW\*(C`("little\en", "fluffy\en", "clouds\en")\*(C'\fR, alors elle afficherait\ : .PP .Vb 3 \& little \& fluffy \& clouds .Ve .PP Si votre tableau contient des lignes, affichez les simplement\ : .PP .Vb 1 \& print @lines; .Ve .SH "AUTHOR AND COPYRIGHT (on Original English Version)" .IX Header "AUTHOR AND COPYRIGHT (on Original English Version)" Copyright (c) 1997\-1999 Tom Christiansen and Nathan Torkington. All rights reserved. .PP When included as an integrated part of the Standard Distribution of Perl or of its documentation (printed or otherwise), this works is covered under Perl's Artistic Licence. For separate distributions of all or part of this \s-1FAQ\s0 outside of that, see perlfaq. .PP Irrespective of its distribution, all code examples here are public domain. You are permitted and encouraged to use this code and any derivatives thereof in your own programs for fun or for profit as you see fit. A simple comment in the code giving credit to the \s-1FAQ\s0 would be courteous but is not required. .SH "TRADUCTION" .IX Header "TRADUCTION" Copyright (c) 1999 Raphae\*:l Manfredi Tous droits re\*'serve\*'s. .PP Cette oeuvre est couverte par la licence artistique de Perl lorsqu'elle fait partie inte\*'grante de la distribution standard de Perl, ou de sa documentation (imprime\*'e ou autre). Pour d'autres modes de distribution de cette \s-1FAQ\s0, en partie ou en totalite\*', voir perlfaq. .PP Inde\*'pendament de sa distribution, tous les exemples de code sont place\*'s dans le domaine publique. Vous e\*^tes autorise\*'s et encourage\*'s a\*` utiliser ce code et ses de\*'rive\*'s dans vos propres programmes, realise\*'s soit pour le plaisir, soit par profit, comme bon vous semble. Une simple mention dans le code cre\*'ditant cette \s-1FAQ\s0 serait une marque de politesse mais n'est pas obligatoire. .Sh "Version" .IX Subsection "Version" Cette traduction franc\*,aise correspond a\*` la version anglaise distribue\*'e avec perl 5.6.0. Pour en savoir plus concernant ces traductions, consultez . .Sh "Traducteur" .IX Subsection "Traducteur" Raphae\*:l Manfredi , Roland Trique <\fIroland.trique@uhb.fr\fR> (mise a\*` jour). .Sh "Relecture" .IX Subsection "Relecture" Re\*'gis Julie <\fIRe\*'gis.Julie@Cetelem.fr\fR>, Ge\*'rard Delafond