=head1 NOME perlfaq5 - File e Formati ($Revision: 1.25 $, $Date: 2008/03/31 21:14:13 $) =head1 DESCRIZIONE Questa sezione tratta l'I/O e tutto ciE che inizia per "f": filehandle, flushing, formati, e footer. =head2 Come faccio a terminare le operazioni di I/O in corso o a privare del buffer un filehandle di output? PerchE devo fare questo? X X X X Il Perl non supporta esattamente l'output privo di buffer (salvo nel caso in cui possiate eseguire C) benchE supporti il "command buffering" [utilizzo del buffer a comando, NdT], nel quale una scrittura fisica viene eseguita dopo ogni comando di output. La libreria standard di I/O del C (stdio) di solito utilizza il buffer per i caratteri inviati alle periferiche cosicchE non ci sia una chiamata di sistema per ogni byte. In molte implementazioni di stdio, il tipo di output con buffer e la dimensione del buffer variano a seconda del tipo di periferica. Le funzioni Perl print() e write() di solito hanno l'output con buffer, mentre syswrite() evita del tutto l'uso del buffer. Se volete che il vostro output venga immediatamente inviato quando eseguite print() o write() (per esempio, per alcuni protocolli di rete), dovete settare il flag di terminazione automatica delle operazioni di I/O in corso dell'handle. Questo flag E costituito dalla variabile Perl $| e quando viene impostata ad un valore vero, il Perl svuoterE il buffer dell'handle dopo ogni print() o write(). Impostare $| influenza l'uso del buffer solo per il filehandle di default attualmente selezionato. Scegliete questo handle con la chiamata select() ad un argomento (vedete L e L). Usate select() per scegliere l'handle desiderato, poi impostate le sue variabili per filehandle. $vecchio_fh = select(OUTPUT_HANDLE); $| = 1; select($vecchio_fh); Alcune espressioni idiomatiche possono gestirlo in una singola istruzione: select((select(OUTPUT_HANDLE), $| = 1)[0]); $| = 1, select $_ for select OUTPUT_HANDLE; Alcuni moduli offrono un accesso orientato agli oggetti agli handle e alle loro variabili, sebbene possa essere eccessivo se questa E l'unica cosa per cui li usate. Potete usare IO::Handle: use IO::Handle; open(DEV, ">/dev/printer"); # ma E questa? DEV->autoflush(1); oppure IO::Socket: use IO::Socket; # questa E tipo una pipe? my $sock = IO::Socket::INET->new( 'www.esempio.com:80' ); $sock->autoflush(); =head2 Come si cambia una riga di un file/cancella una riga di un file/inserisce una riga nel mezzo di un file/aggiunge all'inizio di un file? X Usate il modulo Tie::File, che E incluso nella distribuzione standard fin dal Perl 5.8.0. =head2 Come si conta il numero di righe di un file? X X X Un modo efficente ed elegante E quello di contare i caratteri di ritorno a capo nel file. Il seguente programma usa una caratteristica di tr///, come viene documentata in L. Se le righe del vostro file di testo non terminano con il ritorno a capo, il file non E propriamente un file di testo, quindi il programma potrebbe restiruire meno righe di quello che vi aspettereste. $linee = 0; open(FILE, $nomefile) or die "Non posso aprire '$nomefile': $!"; while(sysread FILE, $buffer, 4096) { $linee += ($buffer =~ tr/\n//); } close FILE; Questo presuppone che non si facciano giochetti con l'uso di tr sul ritorno a capo. =head2 Come posso usare l'opzione del Perl C<-i> dall'interno di un programma? X<-i> X C<-i> imposta il valore della variabile Perl C<$^I>, che a sua volta influisce sul comportamento di C<< <> >>; consultate L per maggiori dettagli. Modificando direttamente le variabili appropriate, potete ottenere lo stesso comportamento all'interno di un grosso programma. Per esempio: # ... { local($^I, @ARGV) = ('.orig', glob("*.c")); while (<>) { if ($. == 1) { print "Questa linea dovrebbe apparire in cima ad ogni file\n"; } s/\b(p)earl\b/${1}erl/i; # Corretto un errore di battitura, mantiene le maiuscole/minuscole print; close ARGV if eof; # Reimposta $. } } # $^I e @ARGV qua ritornano ai loro vecchi valori Questo blocco modifica tutti i file C<.c> nella directory corrente, lasciando una copia del dato originale da qualunque file in un nuovo file C<.c.orig>. =head2 Come posso copiare un file? X X (contributo di brian d foy) Usate il modulo File::Copy. E disponibile con il Perl e puE fare una copia effettiva attraverso i filesystem e fa la sua magia in maniera portabile. use File::Copy; copy( $originale, $nuova_copia ) or die "Copia fallita: $!"; Se non potete usare File::Copy, dovrete farlo voi stessi: aprire il file originale, aprire il file di destinazione, poi scrivere sul file di destinazione quello che avete letto nell'originale. =head2 Come si crea un file temporaneo? X Se non vi serve conoscere il nome del file, potete usare C con C al posto del nome del file. La funzione C crea un file temporaneo anonimo. open my $tmp, '+>', undef or die $!; Altrimenti, potete usare il modulo File::Temp. use File::Temp qw/ filetemp dirtemp /; $dir = dirtemp( CLEANUP => 1 ); ($fh, $nomefile) = filetemp( DIR => $dir ); # oppure, se non avete bisogno del nome del file $fh = filetemp( DIR => $dir ); File::Temp E un modulo standard fin dal Perl 5.6.1. Se avete installato un Perl abbastanza recente, usate il metodo di classe C dal modulo IO::File per ottenere un file aperto in lettura e scrittura. Usate quanto segue se non avete la necessitE di sapere il nome del file: use IO::File; $fh = IO::File->new_tmpfile() or die "Impossibile creare un nuovo file temporaneo: $!"; Se siete costretti a creare un file temporaneo a mano, usate l'ID del processo e/o il valore dell'ora corrente. Se avete bisogno di piE file temporanei in un unico processo, usate un contatore: BEGIN { use Fcntl; my $dir_temp = -d '/tmp' ? '/tmp' : $ENV{TMPDIR} || $ENV{TEMP}; my $nome_base = sprintf("%s/%d-%d-0000", $dir_temp, $$, time()); sub file_temp { local *FH; my $conta = 0; until (defined(fileno(FH)) || $conta ++ > 100) { $nome_base =~ s/-(\d+)$/"-" . (1 + $1)/e; # O_EXCL e` richiesto per ragioni di sicurezza. sysopen(FH, $nome_base, O_WRONLY|O_EXCL|O_CREAT); } if (defined(fileno(FH))) { return (*FH, $nome_base); } else { return (); } } } =head2 Come posso manipolare file contenenti record a lunghezza fissa? X X Il modo piE efficiente E usare pack() e unpack(). E piE veloce rispetto all'uso di substr() quando si ricevono molte, molte stringhe. E piE lento se se ne ricevono poche. Ecco un pezzo di codice d'esempio per dividere in pezzi e poi rimettere insieme alcune linee di input in formato fisso, in questo caso dall'output di un normale ps stile Berkeley: # esempio di linea in input # 15158 p5 T 0:00 perl /home/larsen/script/ my $PS_T = 'A6 A4 A7 A5 A*'; open my $ps, '-|', 'ps'; print scalar <$ps>; my @campi = qw( pid tt stat time command ); while (<$ps>) { my %processo; @processo{@campi} = unpack($PS_T, $_); for my $campo ( @campi ) { print "$campo: <$processo{$campo}>\n"; } print 'line=', pack($PS_T, @processo{@campi} ), "\n"; } Abbiamo usato uno hash slice in maniera da manipolare facilmente i campi di ogni riga. Immagazzinare le chiavi in un array significa che E facile operarvi come gruppo oppure interarvi con un for. Evita anche di inquinare il programma con variabili globali e l'utilizzo di riferimenti simbolici. =head2 Come posso rendere un filehandle locale ad una subroutine? Come faccio a passare filehandle tra subroutine? Come faccio a creare un array di filehandle? X X X Con la versione 5.6 di perl, open() crea automaticamente handle per file e directory sotto forma di riferimenti se gli viene passata una variabile scalare non inizializzata. Potete poi passare tali riferimenti come qualunque altro scalare, e usarli al posto degli handle dotati di nome. open my $fh, $nome_file; open local $fh, $nome_file; print $fh "Salve Mondo!\n"; processa_file( $fh ); Prima della versione 5.6, dovevate avere a che fare con vari idiomi che usavano i typeglob e che potete vedere nel codice meno recente. open FILE, "> $nomefile"; processa_typeglob( *FILE ); processa_riferimento( \*FILE ); sub processa_typeglob { local *FH = shift; print FH "Typeglob!" } sub processa_riferimento { local $fh = shift; print $fh "Riferimento!" } Se volete creare molti handle anonimi, dovreste controllare i moduli Symbol e IO::Handle. =head2 Come posso utilizzare un filehandle in maniera indiretta? X Utilizzare un filehandle in maniera indiretta significa usare qualcosa di diverso da un simbolo in un luogo dove E richiesto un filehandle Di seguito sono riportati alcuni modi per ottenere un filehandle indiretto: $fh = UN_FH; # parola senza virgolette; e` ostile a strict subs $fh = "UN_FH"; # e` ostile a strict-refs; solo nello stesso package $fh = *UN_FH; # typeglob $fh = \*UN_FH; # reference ad un typeglob (bless-abile) $fh = *UN_FH{IO}; # IO::Handle blessed dal typeglob *UN_FH Oppure potete servirvi del metodo C da uno dei moduli IO::* per creare un filehandle anonimo, memorizzarlo in una variabile scalare, ed utilizzarlo come se fosse un normale filehandle. use IO::Handle; # 5.004 o superiore $fh = IO::Handle->new(); Usate poi uno di quelli come fareste con un normale filehandle. Ovunque Perl si aspetti un filehandle, al suo posto puE essere utilizzato un filehanle indiretto. Un filehandle indiretto E semplicemente una variabile scalare che contiene un filehandle. Funzioni quali C, C, C, o l'operatore C<< >> accetteranno sia un filehandle vero e proprio che una variabile scalare che ne contenga uno: ($ifh, $ofh, $efh) = (*STDIN, *STDOUT, *STDERR); print $ofh "Scrivilo: "; $ottenuto = <$ifh>; print $efh "Cos'era quello: $ottenuto"; Se state passando un filehandle ad una funzione, potete scrivere tale funzione in due modi: sub accetta_fh { my $fh = shift; print $fh "Sto inviando ad un filehandle indiretto\n"; } Oppure potete localizzare un typeglob ed utilizzare direttamente il filehandle: sub accetta_fh { local *FH = shift; print FH "Sto inviando ad un filehandle localizzato\n"; } Entrambi gli stili funzionano sia con oggetti che con typeglob che con filehandle reali. (Potrebbero anche funzionare con stringhe in alcune circostanze, ma la cosa E rischiosa.) accept_fh(*STDOUT); accept_fh($handle); Negli esempi sopra riportati, abbiamo assegnato il filehandle ad una variabile scalare prima di utilizzarlo. CiE accade perchE solo le semplici variabili scalari, e non espressioni o elementi di hash oppure array, possono essere usati con le funzioni integrate come C e C, o con l'operatore diamante [, NdT]. L'utilizzo di qualcosa di diverso da una semplice variabile scalare come filehandle non E consentito, ed il programma non compilerE nemmeno: @fd = (*STDIN, *STDOUT, *STDERR); print $fd[1] "Scrivilo: "; # ERRATO $ottenuto = <$fd[0]>; # ERRATO print $fd[2] "Cos'era quello: $ottenuto"; # ERRATO Con C e C potete aggirare ciE servendovi di un blocco ed un'espressione al posto del filehandle: print { $fd[1] } "cose carine\n"; printf { $fd[1] } "Peccato per la povera %x.\n", 3_735_928_559; # Peccato per la povera deadbeef. [convenzionalmente, uno schema esadecimale utilizzato per riempire parole di memoria, NdT] Questo blocco E un blocco valido come qualsiasi altro, quindi potete inserire codice piE complesso al suo interno. Il codice di seguito riportato invia il messaggio in uno dei due posti: $ok = -x "/bin/cat"; print { $ok ? $fd[1] : $fd[2] } "cat stat $ok\n"; print { $fd[ 1+ ($ok || 0) ] } "cat stat $ok\n"; Questo approccio, consistente nel trattare C e C come chiamate a metodi di un oggetto, non funziona con l'operatore diamante. CiE accade perchE esso E un vero operatore, non solo una funzione con argomenti non separati da virgola. Ponendo che abbiate memorizzato i tyeglob nella vostra struttura come indicato in precedenza, potete utilizzare la funzione integrata C per leggere un record allo stesso modo in cui fa C<< <> >>. Posta l'inizializzazione indicata prima per @fd, ciE dovrebbe funzionare, ma solo perchE readline() richiede un typeglob. Non funziona con oggetti o stringhe, il che potrebbe essere un bug che non abbiamo ancora corretto. $ottenuto = readline($fd[0]); Va notato che la debolezza dei filehandle indiretti non E collegata al fatto che essi siano stringhe, typeglob, oggetti, o qualsiasi altra cosa. E la sintassi degli operatori fondamentali. Il gioco degli oggetti non vi E di alcun aiuto qui. =head2 Come posso impostare un formato di fine pagina da utilizzare con write()? X