=head1 NOME perlpacktut - tutorial su C e C =head1 DESCRIZIONE C e C sono due funzioni adatte a trasformare dati secondo un modello definito dall'utente, partendo dal modo vincolato in cui Perl immagazzina i valori verso una qualche rappresentazione ben definita che potrebbe essere richiesto nell'ambito di un programma Perl. Sfortunatamente, sono anche due delle funzioni peggio comprese e spesso ignorate, disponibili in Perl. Questo tutorial provvederE a demistificarvele. =head1 Il Principio Base La maggior parte dei linguaggi di programmazione non protegge la memoria in cui sono immagazzinate le variabili. In C, ad esempio, potete prendere l'indirizzo di una variabile, e l'operatore C vi dirE quanti ottetti le sono allocati. Utilizzando indirizzo e dimensione, potete accedere la memoria che la immagazzina come vi pare e piace. In Perl, non potete accedere la memoria cosE a casaccio, ma la conversione di struttura e di rappresentazione fornita da C e C rappresenta un'eccellente alternativa. La funzione C converte i valori verso una sequenza di ottetti che contiene delle rappresentazioni in base ad una specifica prestabilita, il cosiddetto argomento "modello". C E il processo inverso, in cui si derivano alcuni valori a partire dal contenuto di una stringa di ottetti. (Fate attenzione, comunque, perchE non tutto ciE che viene I puE essere I in maniera pulita - un'esperienza piuttosto comune che i viaggiatori esperti possono facilmente confermare). PerchE, potreste chiedere, si dovrebbe avere bisogno di avere un pezzo di memoria che contiene valori in rappresentazione binaria? Una buona ragione riguarda l'accesso in ingresso ed uscita ad un file, un dispositivo o una connessione di rete, laddove questa rappresentazione binaria E obbligatoria o vi porta qualche beneficio in termini di elaborazione. Un'altra ragione puE essere il passaggio di dati ad una chiamata di sistema che non E disponibile come funzione Perl: C esige che forniate i parametri nel modo in cui si fa in un programma C. Anche l'elaborazione di testi (come vedremo nella prossima sezione) puE risultare piE semplice con un uso oculato di queste due funzioni. Per vedere come funziona l'impacchettamento (o lo spacchettamento), cominciamo con un modello semplice dove la conversione E basilare: tra il contenuto di una sequenza di ottetti ed una stringa di caratteri esadecimali. Utilizziamo C, poichE E facile che vi ricordi un programma di I [termine usualmente utilizzato per indicare la stampa o il salvataggio di un'area di memoria, NdT], o qualche ultimo disperato messaggio quale sono soliti lanciarvi gli sfortunati programmi appena prima di volare in paradiso. Assumendo che la variabile C<$mem> contenga una sequenza di ottetti che vorremmo ispezionare senza fare alcuna ipotesi sul suo significato [ossia, sul suo contenuto, NdT], possiamo scrivere my( $esadecimale ) = unpack( 'H*', $mem ); print "$esadecimale\n"; dove potremmo ritrovarci qualcosa come la seguente, con ciascuna coppia di cifre esadecimali corrispondente ad un singolo ottetto: 41204d414e204120504c414e20412043414e414c2050414e414d41 Cosa c'era in quella zona di memoria? Numeri, caratteri o una mistura dei due? Assumendo di trovarci su un computer dove viene utilizzata la codifica ASCII (o una simile): i valori esadecimali nell'intervallo C<0x40> - C<0x5A> indicano una lettera maiuscola, C<0x20> codifica uno spazio. Possiamo quindi assumere che si tratti di un brano di testo, che alcuni sono in grado di leggere come un giornale; altri, invece, hanno bisogno di mettere le mani su una tabella ASCII e rivivere quella sensazione da pivelli. Senza preoccuparci troppo di come riuscire a leggerlo, osserviamo che C con il codice modello C converte il contenuto di una sequenza di ottetti nella notazione esadecimale. PoichE "una sequenza di" E un'indicazione piuttosto vaga della quantitE di ottetti, C E definito per la conversione di un'unica cifra esadecimale, a meno che non sia seguito da un contatore di ripetizione. Un asterisco come contatore di ripetizione significa utilizzare qualsiasi carattere resti. L'operazione inversa - impacchettare un contenuto in ottetti da una stringa di cifre esadecimali - si scrive altrettanto facilmente. Ad esempio: my $s = pack( 'H2' x 10, map { "3$_" } ( 0..9 ) ); print "$s\n"; PoichE forniamo a C una lista di dieci stringhe esadecimali da due caratteri l'una, il modello di impacchettamento dovrebbe contenere dieci codici. Se lo eseguiamo su un computer con la codifica dei caratteri ASCII, stamperE C<0123456789>. =head1 Impacchettare Testo Supponiamo che dobbiate leggere un file di dati come questo: Data |Descrizione |Entrate|Uscite 01/24/2001 Emporio Cammelli di Ahmed 1147.99 01/28/2001 Spray per le pulci 24.99 01/29/2001 Giro cammello per turisti 1235.00 Come farlo? Per iniziare, potreste pensare di utilizzare C; comunque, poichE C raggruppa campi vuoti, non saprete mai se un record era relativo alle entrate o alle uscite. Oops. Bene, potreste sempre utilizzare C: while (<>) { my $data = substr($_, 0, 11); my $descr = substr($_, 12, 27); my $entrate = substr($_, 40, 7); my $uscite = substr($_, 52, 7); ... } Non E proprio da ridere, eh? Infatti, E peggio di quanto possa sembrare: chi ha la vista aguzza puE aver notato che il primo campo E ampio solo 10 caratteri, e che l'errore si E propagato attraverso gli altri valori - che dobbiamo contare a mano. Quindi, risulta soggetto ad errore cosE come risulta terribilmente ostile. Potremmo anche utilizzare alcune espressioni regolari: while (<>) { my($data, $descr, $entrate, $uscite) = m|(\d\d/\d\d/\d{4}) (.{27}) (.{7})(.*)|; ... } Urgh. Va un po' meglio ma - beh, vi piacerebbe doverci fare manutenzione? Ehi, ma Perl non dovrebbe semplificare questo genere di cose? Beh, lo fa se utilizzate gli strumenti adatti. C e C sono progettati per aiutarvi quando avete a che fare con dati a lunghezza fissa come quelli nell'esempio dato. Diamo un'occhiata alla soluzione con C: while (<>) { my($data, $descr, $entrate, $uscite) = unpack("A10xA27xA7A*", $_); ... } Sembra giE meglio, ma dobbiamo smontare quello strano modello. Da dove l'ho tirato fuori? OK, diamo di nuovo un'occhiata ad un po' dei dati di origine; di fatto, includeremo le intestazioni, ed un comodo righello in modo da tenere traccia di dove ci troviamo. 1 2 3 4 5 1234567890123456789012345678901234567890123456789012345678 Data |Descrizione |Entrate|Uscite 01/24/2001 Emporio Cammelli di Ahmed 1147.99 01/28/2001 Spray per le pulci 24.99 Da questo possiamo vedere che la colonna della data si estende dalla colonna 1 alla colonna 10 - E larga 10 caratteri. "Carattere", in gergo C, corrisponde a C, e dieci caratteri sono quindi C. Se volessimo dunque estrarre le sole date, potremmo scrivere: my($data) = unpack("A10", $_); OK, che viene dopo? Tra la data e la descrizione c'E una colonna vuota; vogliamo saltarla. Il modello C significa "fai un salto in avanti", per cui ce ne serve uno. Di seguito, abbiamo un altro po' di caratteri, dal 12 al 38. In tutto sono 27 caratteri, da cui C. (Non commettete l'errore sui bordi - ci sono 27 caratteri tra 12 e 38, non 26. Contateli!) Ora saltiamo un altro carattere e prendiamo i 7 caratteri successivi: my($data,$descrizione,$entrate) = unpack("A10xA27xA7", $_); Ora arriviamo alla furbata. Le righe nella nostra tabella che contengono solo entrate e non spese potrebbero terminare alla colonna 46. Per questo motivo, non possiamo dire al nostro modello C che B di trovare altri 12 caratteri; diremo solamente che "se avanza qualcosa, prendilo". Come potete immaginare conoscendo le espressioni regolari, questo E quello che C<*> significa: "usa qualunque cosa rimanga". =over 3 =item * Fate attenzione che, diversamente dalle espressioni regolari, se il modello C non corrisponde ai dati in ingresso, Perl comincerE ad urlare e terminerE con C. =back Da qui, mettendo tutto insieme: my($data,$descriz,$entrate,$uscite) = unpack("A10xA27xA7xA*", $_); Ecco infine che i nostri dati sono interpretati. Suppongo che ciE che potremmo desiderare di fare a questo punto sia di trovare il totale di entrate ed uscite, per aggiungere un'altra riga alla fine del nostro prospetto - nello stesso formato - indicando quanto abbiamo ricavato e quanto abbiamo speso: while (<>) { my($data, $descr, $entrate, $uscite) = unpack("A10xA27xA7xA*", $_); $tot_entrate += $entrate; $tot_uscite += $uscite; } $tot_entrate = sprintf("%.2f", $tot_entrate); # Trasformazione in $tot_uscite = sprintf("%.2f", $tot_uscite); # formato "finanziario" $data = POSIX::strftime("%m/%d/%Y", localtime); # OK, possiamo andare print pack("A10xA27xA7xA*", $data, "Totali", $tot_entrate, $tot_uscite); Oh, hmm. Non ha proprio funzionato. Vediamo cos'E successo: 01/24/2001 Emporio Cammelli di Ahmed 1147.99 01/28/2001 Spray per le pulci 24.99 01/29/2001 Giro cammello per turisti 1235.00 03/23/2001Totali 1235.001172.98 OK, E un inizio, ma che E successo agli spazi? Abbiamo messo C, no? Non dovrebbe fare un salto in avanti? Diamo un'occhiata a cosa dice L: x A null byte. [Un ottetto nullo, NdT] Urgh. Nessuna meraviglia che non abbia funzionato. C'E una gran differenza fra "un ottetto nullo", ossia il carattere zero, ed "uno spazio", ossia il carattere 32. Perl ha inserito qualcosa fra data e descrizione - ma sfortunatamente non possiamo vederlo! Quello di cui abbiamo veramente bisgogno E di espandere la larghezza dei campi. Il formato C aggiunge spazi al posto dei caratteri che non esistono, di modo che possiamo utilizzare gli spazi addizionali per allineare i nostri campi, come: print pack("A11 A28 A8 A*", $data, "Totali", $tot_entrate, $tot_uscite); (Osservate che potete inserire spazi nel modello per renderlo piE leggibile, ma che questi non si traducono in spazi in uscita). Ecco quel che abbiamo ottenuto questa volta: 01/24/2001 Emporio Cammelli di Ahmed 1147.99 01/28/2001 Spray per le pulci 24.99 01/29/2001 Giro cammello per turisti 1235.00 03/23/2001 Totali 1235.00 1172.98 Va giE meglio, ma abbiamo ancora l'ultima colonna che va spostata un po' piE in lE. C'E un modo semplice per risolvere questa situazione: sfortunatamente, non possiamo chiedere a C di giustificare i campi sulla destra, ma possiamo usare C: $tot_entrate = sprintf("%.2f", $tot_entrate); $tot_uscite = sprintf("%12.2f", $tot_uscite); $data = POSIX::strftime("%m/%d/%Y", localtime); print pack("A11 A28 A8 A*", $data, "Totali", $tot_entrate, $tot_uscite); Questa volta otteniamo la risposta corretta: 01/24/2001 Emporio Cammelli di Ahmed 1147.99 01/28/2001 Spray per le pulci 24.99 01/29/2001 Giro cammello per turisti 1235.00 03/23/2001 Totali 1235.00 1172.98 Ecco dunque come possiamo leggere e scrivere dati a larghezza fissa. Ricapitolando quel che abbiamo visto di C e C fino ad ora: =over 3 =item * Utilizzate C per andare da una moltitudine di dati ad una versione a larghezza fissa; utilizzate C per trasformare un formato a larghezza fissa in una molteplicitE di dati. =item * Il formato di impacchettamento C significa "un qualsiasi carattere"; se state impacchettando con C e avete finito le cose da impacchettare, C riempirE il resto di spazi. =item * C significa "saltare un ottetto" con C; con C, significa "inserire un ottetto nullo" - che probabilmente non E quello che vi serve quando avete a che fare con del semplice testo. =item * Potete aggiungere dei numeri ai formati per specificare quanti caratteri dovrebbero essere coinvolti da quel dato formato: C significa "prendere 12 caratteri"; C significa "saltare 6 ottetti" o "carattere 0, per 6 volte". =item * Invece di un numero, potete utilizzare C<*> per intendere "leggi tutto ciE che E rimasto". B: quando impacchettate una molteplicitE di dati, C<*> significa solo "leggi tutto quanto avanza del dato corrente". Si intende cioE che: pack("A*A*", $uno, $due) impacchetta tutto C<$uno> nel primo C e poi tutto C<$due> nel secondo. Questo E un principio generale: ciascun carattere di formato corrisponde ad una porzione di dati da impacchettare con C. =back =head1 Impacchettare Numeri Con i dati testuali abbiamo finito. Arriviamo allora alla "ciccia" vera, dove C e C danno il loro meglio: gestire formati binari per i numeri. Ovviamente, non esiste un solo formato binario - la vita sarebbe troppo semplice - ma Perl farE tutto il lavoro certosino al posto vostro. =head2 Interi Impacchettare ed estrarre numeri implica una conversione verso, e da, una qualche rappresentazione binaria I. Lasciando perdere i numeri a virgola mobile per il momento, le proprietE veramente importanti di qualunque rappresentazione del genere sono: =over 4 =item * il numero di ottetti utilizzati per immagazzinare l'intero, =item * se il contenuto va interpretato come numero con segno o privo di segno, =item * l'ordine degli ottetti, ossia se il primo ottetto rappresenta il byte piE significativo o il meno significativo (o anche, rispettivamente, big-endian [finale grande, NdT] o little-endian [finale piccolo, NdT]). =back Quindi, ad esempio, per impacchettare il numero 20302 in un intero a 16 bit con segno, nella rappresentazione del vostro computer, dovete scrivere my $ps = pack( 's', 20302 ); Di nuovo, il risultato E una stringa, che ora contiene due ottetti. Se stampate tale stringa (il che, in generale, non E consigliabile) potreste vedere C oppure C (in base all'ordinamento degli ottetti nel vostro sistema) - o qualcosa di completamente differente se il vostro computer non utilizza la codifica ASCII. Utilizzare C su C<$ps> con lo stesso modello restituisce il valore intero originale: my( $s ) = unpack( 's', $ps ); Tutto ciE vale per tutti i codici di modello numerici. Non vi aspettate miracoli, comunque: se il valore impacchettato eccede la capacitE in ottetti assegnata, i bit piE significativi sono scartati senza colpo ferire, ed C certamente non ve li ritirerE fuori dal cilindro. Inoltre, quando impacchettate utilizzando un codice di modello come C, un valore in eccesso potrebbe avere come conseguenza che il bit di segno venga impostato, per cui lo spacchettamento vi tirerE fuori - molto intelligentemente - un valore negativo. 16 bit non vi porteranno molto lontano con gli interi, ma ci sono sempre C e C per gli interi con e senza segno a 32 bit. E se non vi bastassero nemmeno questi, ed aveste a disposizione interi a 64 bit nel vostro sistema, potete spingere i limiti piE vicino all'infinito con i codici C e C di C. Un'eccezione degna di nota si ha con i codici C e C per gli interi con e senza segno della varietE "locale e personalizzata": un intero che occupa tanti ottetti quanto il compilatore C locale si aspetta con C, ma utilizzerE I 32 bit. Ciascuno dei codici per interi C dE come risultato un numero fisso di ottetti, indipendentemente da dove eseguiate il vostro programma. Questo fatto puE risultare utile per alcune applicazioni, ma non E un modo portabile per passare strutture dati tra Perl ed i programmi C (il che succede di sicuro quando chiamate estensioni XS o la funzione Perl C), o quando leggete o scrivete file binari. CiE di cui avete bisogno in questo caso sono dei codici di modello che rispecchino quello che viene utilizzato dal vostro compilatore C locale quando usate C o C, ad esempio. Tali codici e le relative lunghezze in ottetti sono mostrate nella tabella che segue. PoichE lo standard C lascia molta libertE riguardo alle dimensioni relative di questi tipi di dato, i valori effettivi possono variare, e questo E il motivo per cui i valori sono indicati attraverso espressioni in C ed in Perl. (Se volete utilizzare i valori da C<%Config> nel vostro programma dovete importarla con C). con segno senza segno otteti, in C ottetti, in Perl s! S! sizeof(short) $Config{shortsize} i! I! sizeof(int) $Config{intsize} l! L! sizeof(long) $Config{longsize} q! Q! sizeof(long long) $Config{longlongsize} I codici C e C non sono differneti da C e C, sono solo inclusi per completezza. =head2 Spacchettare un Frame dello Stack [lo Stack E la pila di attivazione di un programma, composta da tanti "mattoni" chiamati Frame, NdT] Avere bisogno di un particolare ordinamento degli ottetti potrebbe esservi necessario quando lavorate con dati binari che provengono da una qualche architettura specifica, laddove il vostro programma potrebbe trovarsi a lavorare in un sistema completamente differente. Come esempio, immaginate di avere 24 ottetti che contengono uno I come avviene in un 8086 Intel: +---------+ +----+----+ +---------+ TOS: | IP | TOS+4:| FL | FH | FLAGS TOS+14:| SI | +---------+ +----+----+ +---------+ | CS | | AL | AH | AX | DI | +---------+ +----+----+ +---------+ | BL | BH | BX | BP | +----+----+ +---------+ | CL | CH | CX | DS | +----+----+ +---------+ | DL | DH | DX | ES | +----+----+ +---------+ Prima di tutto, osserviamo che questa veneranda CPU a 16 bit utilizza un ordinamento a finale piccolo [I, NdT], e che questo E il motivo per cui l'ottetto di ordine basso E immagazzinato all'indirizzo piE basso. Per spacchettare un intero I (con segno) di tal fatta dovremo utilizzare il codice C. Un contatore di ripetizione ci consente di spacchettare tutti e 12 gli interi I: my( $ip, $cs, $flags, $ax, $bx, $cd, $dx, $si, $di, $bp, $ds, $es ) = unpack( 'v12', $frame ); In alternativa, avremmo potuto utilizzare C per spacchettare i registri byte FL, FH, AL, AH, ecc., individualmente accessibili: my( $fl, $fh, $al, $ah, $bl, $bh, $cl, $ch, $dl, $dh ) = unpack( 'C10', substr( $frame, 4, 10 ) ); Sarebbe carino se potessimo farlo in un solo passo: spacchettare uno I, tornare indietro un po', e poi spacchettare 2 ottetti. Visto che Perl I> carino, mette a disposizione il codice di modello C per tornare indietro di un ottetto. Mettendo tutto insieme, possiamo allora scrivere: my( $ip, $cs, $flags,$fl,$fh, $ax,$al,$ah, $bx,$bl,$bh, $cx,$cl,$ch, $dx,$dl,$dh, $si, $di, $bp, $ds, $es ) = unpack( 'v2' . ('vXXCC' x 5) . 'v5', $frame ); (La costruzione astrusa del modello puE essere evitata - leggete!) Ci siamo presi la briga di costruire il modello in modo che corrisponda al contenuto del nostro I. Altrimenti o avremmo ottenuto valori indefiniti, o C non avrebbe potuto fare proprio niente. Se C rimane a corto di elementi, inserirE stringhe vuote (che sono trasformate in zeri laddove il codice di impacchettamento lo richiede). =head2 Come Mangiare un Uovo su una Rete Il codice di impacchettamento per il finale grande [I, NdT] (ottetto di ordine alto all'indirizzo piE basso) E C per interi a 16 bit e C per quelli a 32 bit. Utilizzate questi codici se sapete che i vostri dati arrivano da un'architettura compatibile ma, abbastanza sorprendentemente, dovreste anche utilizzare questi codici se scambiate dati binari, in una rete, con qualche sistema di cui non sapete niente. La ragione piE semplice E che questo ordine E stato scelto come I, e tutti i bravi programmi timorati degli standard dovrebbero seguire questa convenzione. (Questo E, chiaramente, un saldo supporto per uno dei partiti Lillipuziani, e puE ben influire sugli sviluppi politici di Lilliput). Quindi, se il protocollo prevede che mandiate un messaggio inviando prima di tutto la sua lunghezza, seguita da quel tal numero di ottetti, potreste scrivere: my $buf = pack( 'N', length( $msg ) ) . $msg; o persino: my $buf = pack( 'NA*', length( $msg ), $msg ); e passare C<$buf> alla vostra routine di invio. Alcuni protocolli richiedono che il conteggio includa anche la lunghezza del contatore stesso: in questo caso basterE aggiungere 4 alla lunghezza dei dati. (Ma assicuratevi di leggere L<"Lunghezze e Larghezze"> prima di fare una cosa del genere!) =head2 Numeri a Virgola Mobile Per impacchettare numeri in virgola mobile dovete scegliere fra i codici C e C, che impacchettano (o spacchettano) in rappresentazioni a singola precisione o doppia precisione, cosE come fornite dal vostro sistema. (Non esiste una vera rappresentazione di rete per i reali, quindi se volete inviare i vostri numeri reali oltre i confini di un computer, E meglio se vi attenete ad una rappresentazione ASCII, a meno che non siate assolutamente sicuri di cosa trovate dall'altra parte della linea). =head1 Modelli Esotici =head2 Stringhe di Bit I bit sono gli atomi del mondo della memoria. L'accesso ai bit individuali potrebbe essere necessario o come ultima spiaggia o perchE E il modo piE conveniente di trattare i vostri dati. L'impacchettamento (o lo spacchettamento) in stringhe di bit converte fra stringhe che contengono una serie di caratteri C<0> ed C<1> e una sequenza di ottetti, ognuna contenente un gruppo di 8 bit. E proprio semplice come sembra, ad eccezione del fatto che ci sono due modi in cui il contenuto di un ottetto puE essere scritto come stringa di bit. Diamo un'occhiata ad un ottetto annotato: 7 6 5 4 3 2 1 0 +-----------------+ | 1 0 0 0 1 1 0 0 | +-----------------+ MSB LSB [MSB sta per I, ossia il bit piE significativo, quello di peso piE alto; analogamente, LSB sta per L, ossia bit meno significativo. NdT] Si tratta di nuovo di mangiar uova: alcuni pensano che, come stringa di bit, questo ottetto debba essere scritto come "10001100", ossia a partire dal bit piE significativo, mentre altri insistono per avere "00110001". Bene, Perl non fa preferenze, per cui abbiamo due codici per le stringhe di bit: $byte = pack( 'B8', '10001100' ); # inizia con MSB $byte = pack( 'b8', '00110001' ); # inizia con LSB Non E possibile impacchettare o spacchettare campi di bit - solo ottetti interi. C parte sempre dal bordo dell'ottetto successivo, ed "arrotonda" al multiplo successivo di 8 aggiungendo degli zero se necessario. (Se proprio volete i campi di bit, esiste sempre la funzione L. In alternativa, potreste implementare la gestione del campo di bit a livello di stringa di caratteri, utilizzando C, C e la concatenazione su stringhe di bit spacchettati). Per illustrare lo spacchettamento di stringhe di bit, effettueremo la decomposizione di un semplice registro di stato (un carattere "-" indica un bit "riservato"): +-----------------+-----------------+ | S Z - A - P - C | - - - - O D I T | +-----------------+-----------------+ MSB LSB MSB LSB La conversione di questi due ottetti in una stringa puE essere fatta con il modello di spacchettamento C<'b16'>. Per ottenere i valori dei singoli bit dalla stringa di bit utilizziamo C con il pattern di separazione vuoto, che suddivide la stringa nei singoli caratteri. I valori dei bit dalle posizioni "riservate" sono assegnate semplicemente ad C, una notazione piuttosto comoda per indicare "Non mi importa di che fine faccia questo". ($carry, undef, $parity, undef, $auxcarry, undef, $zero, $sign, $trace, $interrupt, $direction, $overflow) = split( //, unpack( 'b16', $status ) ); Avremmo anche potuto utilizzare un modello di spacchettamento C<'b12'>, poichE gli ultimi 4 bit possono essere ignorati. =head2 Uuencode Un altro strano tipo nell'alfabeto dei modelli di impacchettamento e spacchettamento E C, che impacchetta una "stringa con uuencode". ("uu" E l'abbreviazione di Unix-to-Unix [da Unix a Unix, NdT]). Ci sono buone probabilitE che non avrete mai bisogno di questa tecnica di encoding, che fu inventata per superare le limitazioni di mezzi trasmissivi datati che non supportano altro che semplici dati ASCII. La ricetta essenziale E semplice: prendete tre ottetti, o 24 bit. Divideteli in 4 pacchetti da sei bit l'uno, aggiungendo uno spazio (0x20) a ciascuno. Ripetete finchE non avete mescolato tutti i dati. Ripiegate gruppi di 4 ottetti in righe non piE lunghe di 60 ottetti, e guarnite aggiungendo all'inizio il conteggio degli ottetti originali (incrementati con 0x20), e un C<"\n"> alla fine. - Lo chef di C preparerE tutto questo al posto vostro, espresso, quando selezionate il codice di impacchettamento C dal menu: my $uubuf = pack( 'u', $bindat ); Un indicatore di ripetizione dopo C imposta il numero di ottetti da inserire in una riga dopo l'encoding uu, che E il massimo di 45 per default, ma potrebbe essere impostato ad un qualsiasi multiplo intero di tre inferiore a 45. C ignora l'indicatore di ripetizione. =head2 Fare le Somme Un codice di modello persino piE strano E C<%>EIE. Prima di tutto, perchE viene utilizzato come prefisso per qualche altro codice di modello. Secondo poi, perchE non puE mai essere utilizzato in C, e terzo, in C, non restituisce il dato definito dal codice di modello che lo segue. Invece, vi darE un intero contenente il I di bit che risulta dal valore del dato mediante delle somme. Per codici di spacchettamento numerici, non si ha nessuna caratteristica particolare: my $buf = pack( 'iii', 100, 20, 3 ); print unpack( '%32i3', $buf ), "\n"; # stampa 123 Per i valori stringa, C<%> restituisce la somma dei valori degli ottetti, levandovi dall'impaccio di effettuare un ciclo di somma con C e C print unpack( '%32A*', "\x01\x10" ), "\n"; # prints 17 Sebbene la documentazione del codice C<%> riporti che esso restituisce un "checksum" [una somma di controllo, N.d.T.]: non vi fidate di questo valore! Anche quando applicato ad un esiguo numero di ottetti, non vi garantirE una distanza di Hamming notevole. In connessione con C o C, C<%> non fa altro che aggiungere bit, e questo puE essere utilizzato per contare insiemi di bit in maniera efficiente: my $conteggiobit = unpack( '%32b*', $mask ); Ed un bit di paritE puE essere determinato come segue: my $parita = unpack( '%1b*', $mask ); =head2 Unicode Unicode E un insieme di caratteri che puE rappresentare la maggior parte dei caratteri nella maggior parte delle lingue al mondo, avendo spazio per piE di un milione di caratteri differenti. Unicode 3.1 specifica 94140 caratteri: i caratteri Basic Latin [Latino base, NdT] sono posizionati ai numeri 0 - 127. Il Supplemento Latin-1, contenente caratteri che sono utilizzati in molti linguaggi europei, si trovano nell'intervallo successivo, fino a 255. Dopo qualche altra estensione Latin troviamo gli insiemi di caratteri da linguaggi che utilizzano alfabeti non Romani, intervallati con una varietE di insiemi di simboli come i segni per le monete, Zapf Dingbat e Braille. (Potreste sempre fare una visita su L per dare un'occhiata a qualcuno - i miei preferiti sono Telugu e Kannada). L'insieme di caratteri Unicode associa caratteri ed interi. La codifica di questi numeri in un numero equivalente di ottetti avrebbe l'effetto di piE che raddoppiare i requisiti di immagazzinamento di testi scritti con alfabeti latini. La codifica UTF-8 evita questo spreco immagazzinando i caratteri piE comuni (da un punto di vista del mondo occidentale) in un singolo ottetto, mentre i piE rari sono immagazzinati in tre o piE ottetti. Dunque, che cosa ha a che fare tutto ciE con C? Bene, se volete convertire fra un numero Unicode e la sua rappresentazione UTF-8, potete farlo utilizzando il codice C. Come esempio, generiamo la rappresentazione UTF-8 del simbolo dell'Euro (codice 0x20AC): $UTF8{Euro} = pack( 'U', 0x20AC ); Andando a vedere C<$UTF8{Euro}> possiamo notare che contiene tre ottetti: "\xe2\x82\xac". Il giro puE essere completato con C: $Unicode{Euro} = unpack( 'U', $UTF8{Euro} ); Di solito vorrete impacchettare o spacchettare intere stringhe UTF-8: # pack e unpack dell'alfabeto Ebraico my $alefbeto = pack( 'U*', 0x05d0..0x05ea ); my @ebraico = unpack( 'U*', $utf ); =head2 Un'altra Codifica Binaria Portabile Il codice di impacchettamento C E stato aggiunto per supportare uno schema di codifica che va al di lE dei semplici interi. (Potete trovare maggiori dettagli su L, il progetto Scarab). Un intero senza segno compresso BER (Binary Encoded Representation [Rappresentazine Binaria Codificata, NdT] contiene cifre in base 128, con la cifra piE significativa per prima, utilizzando meno cifre possibile. L'ottavo bit (quello piE significativo) E impostato ad 1 su tutti gli ottetti eccetto l'ultimo. Non esiste un limite di dimensione per la codifica BER, ma Perl non si spingerE fino agli estremi. my $berbuf = pack( 'w*', 1, 128, 128+1, 128*128+127 ); Una stampa esadecimale di C<$berbuf>, con spazi inseriti nei posti giusti, E 01 8100 8101 81807F. PoichE l'ultimo ottetto E sempre inferiore a 128, C sa dove fermarsi. =head1 Raggruppamento di Modelli Prima di Perl 5.8, la ripetizione dei modelli doveva esser fatta con l'utilizzo dell'operatore C sulle stringhe dei modelli stessi. Ora esiste un modo migliore, perchE possiamo utilizzare i codici C<(> e C<)> in combinazione con un contatore di ripetizione. Il modello C dall'esempio sullo Stack Frame puE scriversi semplicemente cosE: unpack( 'v2 (vXXCC)5 v5', $frame ) Diamo un'occhiata piE da vicino a questa opportunitE. Cominciamo con l'equivalente di join( '', map( substr( $_, 0, 1 ), @str ) ) che restituisce una stringa composta del primo carattere di ciascuna stringa. Utilizzando C, possiamo scrivere pack( '(A)'.@str, @str ) o, viso che il contatore di ripetizione C<*> significa "ripeti quanto basta", semplicemente pack( '(A)*', @str ) (Osservate che il modello C abrebbe solo impacchettato C<$str[0]> a lunghezza piena). Per impacchettare le date immagazzinandole come triplette (giorno, mese, anno) da un array C<@date> in una sequenza di ottetto, otteto, short [intero breve, costituito da 2 ottetti, NdT] possiamo scrivere $pd = pack( '(CCS)*', map( @$_, @date ) ); Per invertire coppie di caratteri in una stringa (di lunghezza pari) possono essere utilizzate varie tecniche. Per prima cosa, utilizziamo C e C per saltare avanti ed indietro: $s = pack( '(A)*', unpack( '(xAXXAx)*', $s ) ); Possiamo anche utilizzare C<@> per saltare ad un determinato scostamento nella stringa, con 0 che rappresenta la posizione dove ci trovavamo l'ultima volta che si E encontrato C<(>: $s = pack( '(A)*', unpack( '(@1A @0A @2)*', $s ) ); Infine, esiste un approccio completamente differente basato sullo spacchettamento di interi short a finale grande e nel successivo re-impacchettamento in ordine di ottetti invertiti: $s = pack( '(V)*', unpack( '(n)*', $s ); =head1 Lunghezze e larghezze =head2 Lunghezza delle stringhe Nella sezione precedente abbiamo visto un messaggio di rete costruito mettendo la lunghezza del messaggio binario come prefisso al messaggio vero e proprio. Vi capiterE di trovare che l'impacchettamento di una lunghezza seguita da quel certo numero di ottetti di dati E una ricetta utilizzata comunemente, perchE aggiungere un ottetto nullo in fondo non funziona correttamente se gli ottetti nulli possono far parte dei dati stessi. Ecco un esempio dove vengono utilizzate entrambe le tecniche: dopo due stringhe terminate con ottetti nulli, viene mandato uno I (ad un terminale mobile) dopo un ottetto che indica la lunghezza: my $msg = pack( 'Z*Z*CA*', $src, $dst, length( $sm ), $sm ); Lo spacchettamento di questo messaggio puE essere effettuato con lo stesso modello: ( $src, $dst, $len, $sm ) = unpack( 'Z*Z*CA*', $msg ); C'E una sottile trappola che si aggira: aggiungere un altro campo dopo lo I (nella variabile C<$sm>) va bene in fase di impacchettamento, ma non puE essere spacchettato alla leggera: # Impacchetta un messaggio my $msg = pack( 'Z*Z*CA*C', $src, $dst, length( $sm ), $sm, $prio ); # unpack fallisce - $prio rimane non definito! ( $src, $dst, $len, $sm, $prio ) = unpack( 'Z*Z*CA*C', $msg ); Il codice di impacchettamento C cattura tutti gli ottetti rimanenti, e C<$prio> rimane non definito! Prima di lasciarci abbattere il morale: Perl ha l'asso per fare anche questo, solo che sta un po' piE su... nella manica. Guardate qui: # impacchetta un messaggio: ASCIIZ, ASCIIZ, lunghezza/stringa, ottetto my $msg = pack( 'Z* Z* C/A* C', $src, $dst, $sm, $prio ); # spacchetta ( $src, $dst, $sm, $prio ) = unpack( 'Z* Z* C/A* C', $msg ); Combinando due codici con una barra (C) si associano ad un singolo valore dalla lista degli argomenti. In C, la lunghezza dell'argomento viene presa ed impacchettata in accordo al primo codice, mentre l'argomento stesso viene aggiunto con la conversione del codice dopo la barra. Il tutto ci risparmia la chiamata a C, ma in C che abbiamo il vero vantaggio: il valore dell'ottetto della lunghezza segna la fine della stringa che deve essere estratta dal buffer. Questa combinazione non ha senso eccetto che nei casi in cui il secondo codice di impacchettamento sia C, C o C. Il codice di impacchettamento che precede C puE essere uno qualsiasi fra quelli adatti a rappresentare un numero: tutti i codici di impacchettamento binario dei numeri, e perfino codici testuali come C o C: # pack/unpack di una stringa preceduta dalla sual lunghezza in ASCII my $buf = pack( 'A4/A*', "Humpty-Dumpty" ); # unpack $buf: '13 Humpty-Dumpty' my $txt = unpack( 'A4/A*', $buf ); C non E presente nelle versioni di Perl precedenti a 5.6, per cui se il vostro codice deve essere eseguito in Perl piE vecchi avrete bisogno di utilizzare C per prendere la lunghezza, e poi utilizzare questa per costruire una nuova stringa di spacchettamento. Ad esempio # impacchetta un messaggio: ASCIIZ, ASCIIZ, lunghezza, stringa, ottetto # (compatibile con la versione 5.005 di Perl) my $msg = pack( 'Z* Z* C A* C', $src, $dst, length $sm, $sm, $prio ); # spacchetta ( undef, undef, $len) = unpack( 'Z* Z* C', $msg ); ($src, $dst, $sm, $prio) = unpack ( "Z* Z* x A$len C", $msg ); Con il secondo C stiamo correndo un po' troppo. Non sta utilizzando una semplice stringa letterale come modello. Per questo motivo, forse E il caso di presentarvi... =head2 Modelli Dinamici Fino ad ora, abbiamo visto modelli composti di soli letterali. Se la lista degli elementi di impacchettamento non ha una lunghezza prefissata, c'E bisogno di un'espressione che costruisca il modello (in tutti quei casi in cui, per qualche motivo, non sia possibile utilizzare C<()*>). Ecco un esempio: per immagazzinare stringhe con un nome associato in modo che possano essere facilmente comprese da un programma C, creiamo una sequenza di nomi e stringhe ASCII terminate da un ottetto nullo, con C<=> utilizzato come separatore fra nome e valore, seguito da un ottetto nullo addizionale. Ecco come: my $env = pack( '(A*A*Z*)' . keys( %Env ) . 'C', map( { ( $_, '=', $Env{$_} ) } keys( %Env ) ), 0 ); Diamo un'occhiata ai dentini di questo ingranaggio, uno ad uno. C'E la chiamata a C, che crea gli elementi che vogliamo infilare nel buffer C<$env>: per ciascuna chiave (in C<$_>) aggiunge il separatore C<=> ed il valore nella hash. Ciascuna tripletta viene impacchettata con la sequenza di modello C che viene ripetuta in base al numero di chiavi. (Esatto, E ciE che la funzione C restituisce in contesto scalare). Per avere l'ultimo ottetto nullo di conclusione, aggiungiamo un C<0> alla fine della lista di C, per impacchettarlo con C. (I lettori piE attenti potranno aver notato che avremmo potuto anche risparmiarci lo 0). Per l'operazione inversa, dovremo determinare il numero di elementi nel buffer prima di lasciare che C li tiri fuori: my $n = $env =~ tr/\0// - 1; my %env = map( split( /=/, $_ ), unpack( "(Z*)$n", $env ) ); La chiamata a C conta gli ottetti nulli. La chiamata a C restituisce una lista di coppie nome-valore, ciascuna delle quali viene separata nel blocco C. =head2 Contare le ripetizioni Piuttosto che aggiungere una sentinella alla fine di un dato (o di una lista di elementi), potremmo precedere i dati con un conteggio. Di nuovo, impacchettiamo chiavi e valori di una hash, precedendo ciascuno con un contatore di lunghezza di tipo intero corto senza segno [unsigned short, NdT], ed all'inizio immettiamo il numero di coppie: my $env = pack( 'S(S/A* S/A*)*', scalar keys( %Env ), %Env ); Questo semplifica l'operazione inversa, poichE il numero di ripetizioni puE essere estratto con il codice C: my %env = unpack( 'S/(S/A* S/A*)', $env ); Osservate che questo E uno dei rari casi nei quali non potete utilizzare lo stesso modello per C e C perchE C non E in grado di determinare un contatore di ripetizione per il gruppo C<()>. =head1 Impacchettare e Spacchettare Strutture C Nelle sezioni precedenti abbiamo visto come impacchettare numeri e stringhe di caratteri. Se non fosse per un paio di trappole potremmo concludere qui, con una nota chiara che le strutture C non contengono nient'altro, e che perciE giE sapete tutto quello che c'E da sapere. Spiacenti, non E cosE: leggete e capirete. =head2 La Fossa dell'Allineamento In un confronto fra requisiti velocitE e memoria la bilancia E stata spostata verso un'esecuzione piE rapida. CiE ha influenzato il modo in cui i compilatori C allocano la memoria per le strutture: su architetture nelle quali operandi a 16 e 32 bit possono essere spostati piE rapidamente fra locazioni di memoria, oppure da/verso registri della CPU, quando questi siano allineati ad indirizzi pari, o multipli di quattro o persino multipli di otto ottetti, un compilatore vi darE questo beneficio di velocitE inserendo ottetti extra nelle strutture. Se non attraversate la banchina del C non vi darE problemi (sebbene dobbiate aver cura quando progettate strutture dati particolarmente grandi, o volete che il vostro codice sia portabile fra le varie architetture (e voi lo volete, giusto?)). Per vedere come questo influisca su C e C, confronteremo le seguenti strutture C: typedef struct { char c1; short s; char c2; long l; } groviera_t; typedef struct { long l; short s; char c1; char c2; } densa_t; Tipicamente, un compilatore C alloca 12 ottetti per una variabile C, ma richiede solo 8 ottetti per una C. Dopo un po' di studio, possiamo disegnare le mappe di memoria, che mostrano dove sono nascosti questi 4 ottetti aggiuntivi: 0 +4 +8 +12 +--+--+--+--+--+--+--+--+--+--+--+--+ |c1|xx| s |c2|xx|xx|xx| l | xx = ottetto di riempimento +--+--+--+--+--+--+--+--+--+--+--+--+ groviera_t 0 +4 +8 +--+--+--+--+--+--+--+--+ | l | h |c1|c2| +--+--+--+--+--+--+--+--+ densa_t E qui E dove colpisce la prima stranezza: i modelli di C e C devono essere riempiti con codici C per arrivare a questi ottetti di riempimento addizionali. L'ovvia domanda: "PerchE Perl non compensa da solo questi buchi?" merita una risposta. Una buona ragione E che il compilatore C potrebbe fornire delle estensioni (non ANSI) che consentono tutte le varietE di controllo sul modo in cui le strutture vengono allineate, persino a livello di singolo campo della struttura. E, se non fosse giE abbastanza, esiste una cosa insidiosa chiamata C dove il numero di ottetti di riempimento non puE essere dedotta solo dall'allineamento dell'elemento del prossimo elemento. OK, via il dente, via il dolore. Ecco un modo per sistemare l'allineamento inserendo i codici di modello C, che non prendono elementi dalla lista: my $gappy = pack( 'cxs cxxx l!', $c1, $s, $c2, $l ); Osservate il C dopo C: vogliamo essere sicuri che impacchettiamo un intero lungo [long integer, NdT] come fatto dal nostro compilatore C. Persino ora, perE, funzionerE solamente nelle piattaforme dove il compilatore allinea come descritto in precedenza. E qualcuno, da qualche parte, usa una piattaforma che non lo fa. (Probabilmente un Cray, dove C, C e C sono tutti di 8 ottetti :-)). Contare gli ottetti e star dietro agli allineamenti in strutture corpose E destinato ad essere un peso. Non esiste un modo con cui possiamo creare un modello utilizzando un semplice programma? Ecco un programmino C che fa questo trucco: #include #include typedef struct { char fc1; short fs; char fc2; long fl; } groviera_t; #define Pt(struttura,campo,tchar) \ printf( "@%d%s ", offsetof(struttura,campo), # tchar ); int main() { Pt( groviera_t, fc1, c ); Pt( groviera_t, fs, s! ); Pt( groviera_t, fc2, c ); Pt( groviera_t, fl, l! ); printf( "\n" ); } La linea stampata in uscita puE essere utilizzata come modello nella chiamata a C o C: my $groviera = pack( '@0c @2s! @4c @8l!', $c1, $s, $c2, $l ); Cribbio, un altro codice di modello - come se non ne avessimo abbastanza. Ma C<@> ci salva la giornata dandoci modo di specificare gli scostamenti dall'inizio del buffer di pack fino all'elemento successivo: questo E appunto il valore restituito dalla macro C (definita in Cstddef.hE>) quando le viene dato un tipo struttura ed uno dei nomi dei suoi campi (in linguaggio standard C il I [indicatore di membro, NdT]). NE l'utilizzo degli scostamenti nE l'aggiunta di C E soddisfacente per riempire i buchi. (Provate solo ad immaginare cosa succederebbe se cambiasse la struttura). CiE di cui abbiamo realmente bisogno E un sistema per dire "salta quel tanto di ottetti che basta per avere il prossimo multiplo di N". In Modellese fluente, potete dirlo con C, ove N va rimpiazzato con il valore appropriato. Ecco una nuova versione del nostro sistema di impacchettamento per strutture: my $groviera = pack( 'c x!2 s c x!4 l!', $c1, $s, $c2, $l ); Andiamo indubbiamente meglio, ma dobbiamo ancora sapere quanto sono lunghi gli interi, e siamo lontani dalla portabilitE. Piuttosto che C<2>, ad esempio, vogliamo esprimere "qualsiasi lunghezza sia uno short". Ma questo possiamo farlo racchiudendo il codice di impacchettamento appropriato fra parentesi quadre: C<[s]>. Quindi, ecco il meglio che possiamo fare: my $groviera = pack( 'c x![s] s c x![l!] l!', $c1, $s, $c2, $l ); =head2 Allineamento, seconda ripresa Ho paura che non abbiamo ancora finito con questa storia dell'allineamento. L'idra solleva un'altra brutta testaccia quando impacchettate array di strutture: typedef struct { short conteggio; char glifo; } cella_t; typedef cella_t buffer_t[BUFLEN]; Dove sta la trappola? Il padding [l'operazione di aggiunta di ottetti per raggiungere una determinata lunghezza, NdT] non E richiesto nE prima del primo campo C, nE fra questo ed il campo C successivo, quindi perchE non possiamo impacchettare semplicemente come segue: # something goes wrong here: pack( 's!a' x @buffer, map{ ( $_->{conteggio}, $_->{glifo} ) } @buffer ); Questo impacchetta C<3*@buffer> ottetti, ma alla fine abbiamo che la dimensione di C E quattro volte C! Il morale della storia E che l'allineamento richiesto per una struttura o un array viene propagato al livello superiore successivo dove dobbiamo considerare di effettuare I anche I di ciascun componente. Per questo motivo, il modello corretto E: pack( 's!ax' x @buffer, map{ ( $_->{conteggio}, $_->{glifo} ) } @buffer ); =head2 Alignment, terza ripresa Ed anche tenendo tutto questo in conto, ANSI consente ancora che questo: typedef struct { char pippo[2]; } pippo_t; possa avere dimensione variabile. Il vincolo di allineamento della struttura puE essere maggiore di ciascuno dei suoi elementi. (E se pensate che questo non abbia impatti su niente di comune, aprite il prossimo telefono cellulare che vedete. Molti hanno processori ARM, e le regole di struttura ARM sono tali che C == 4). =head2 Puntatori per Come Utilizzarli Il titolo di questa sezione indica il secondo problema in cui potete imbattervi prima o poi, quando impacchettate strutture C. Se la funzione che intendete chiamare si aspetta di ricevere, diciamo, un valore C, I prendere semplicemente un riferimento ad una variabile Perl. (Sebbene il valore sia di sicuro un indirizzo di memoria, non E l'indirizzo dove sono immagazzinati i contenuti della variabile). Il codice di modello C

si impegna ad impacchettare un "puntatore ad una stringa di lunghezza fissa". Non E quel che vogliamo? Proviamo: # alloca un po' di spazio ed impacchetta un puntatore ad esso my $memoria = "\x00" x $dimensione; my $memptr = pack( 'P', $memoria ); Aspettate: C non restituisce semplicemente una sequenza di ottetti? Come facciamo a passare questa stringa di ottetti ad un qualche codice C che si aspetta un puntatore che, dopo tutto, altro non E che un numero? La risposta E semplice: otteniamo l'indirizzo numerico dagli ottetti restituiti da C. my $ptr = unpack( 'L!', $memptr ); Ovviamente si sta assumendo che sia possibile effettuare una forzatura di tipo da puntatore ad intero lungo senza segno, e viceversa, il che funziona di frequente ma che non puE essere preso come legge universale. Ora che abbiamo questo puntatore la prossima domanda E: come utilizzarlo per bene? Abbiamo bisogno di chiamare una funzione C che si aspetta di ricevere un puntatore. Ci viene in mente la chiamata di sistema read(2): ssize_t read(int fd, void *buf, size_t count); Dopo aver letto come utilizzare C in C, possiamo scrivere questa funzione Perl che copia un file sullo I: require 'syscall.ph'; sub cat($){ my $percorso = shift(); my $dimensione = -s $path; my $memoria = "\x00" x $dimensione; # alloca un po' di memoria my $ptr = unpack( 'L', pack( 'P', $memoria ) ); open( F, $percorso ) || die( "$path: errore open ($!)\n" ); my $fd = fileno(F); my $res = syscall( &SYS_read, fileno(F), $ptr, $dimensione ); print $memoria; close( F ); } Non E nE un esempio di semplicitE nE un paragone di portabilitE, ma descrive bene la situazione: siamo in grado di scivolare dietro le quinte per accedere alla altrimenti ben sorvegliata memoria di Perl! (Una nota importante: la funzione C di Perl I vi richiede di costruire i puntatori in questa maniera complicata. Passate semplicemente una variabile stringa, e Perl fa il resto inoltrando il suo indirizzo). Come funziona C con C

? Immaginate un puntatore qualsiasi nel buffer che si sta per spacchettare: se non si tratta del puntatore nullo (che molto intelligentemente produrrE un valore C) abbiamo un indirizzo di partenza - ma poi? Perl non ha modo di sapere quanto sia lunga questa "stringa di lunghezza fissa"), per cui sta a voi specificare la reale dimensione come lunghezza esplicita dopo C

. my $mem = "abcdefghijklmn"; print unpack( 'P5', pack( 'P', $mem ) ); # stampa "abcde" Di conseguenza, C ignora qualsiasi numero o C<*> dopo C

. Ora che abbiamo visto C

al lavoro, potremmo fare un giro su C

. PerchE abbiamo bisogno di un secondo codice di modello per impacchettare i puntatori? La risposta giace dietro il semplice fatto che una C con C

si impegna a restituire una stringa terminata da un ottetto nullo a partire dall'indirizzo preso dal buffer, e questo implica una lunghezza per l'elemento di dati che deve essere restituito: my $buf = pack( 'p', "abc\x00efhijklmn" ); print unpack( 'p', $buf ); # stampa "abc" In ogni caso questo porta a della confusione: come risultato del fatto che la lunghezza E conseguenza della lunghezza della stringa, un numero dopo il codice di impacchettamento C

E un contatore di ripetizione, non una lunghezza come dopo C

. L'utilizzo di C con C

o C

per ottenere l'indirizzo dove C<$x> E effettivamente immagazzinata, deve essere effettuato con circospezione. Il codice interno di Perl considera la relazione fra una variabile e quell'indirizzo un qualcosa di veramente privato, e si disinteressa se ne abbiamo ottenuto una copia. PerciE: =over 4 =item * Non utilizzate C con C

o C

per ottenere l'indirizzo di una variabile che finirE fuori dal campo di visibilitE [scope, NdT] (e la cui memoria sarE pertanto liberata) prima che abbiate terminato di utilizzare la memoria a quell'indirizzo. =item * State molto attenti alle operazioni Perl che cambiano il valore della variabile. Aggiungere qualcosa alla variabile, ad esempio, potrebbe richiedere una riallocazione, lasciandovi con un puntatore verso la terra di nessuno. =item * Non pensate di poter accedere al'indirizzo di una variabile Perl quando E immagazzinata come numero intero o a doppia precisione! C forzerE la rappresentazione interna della variabile su una stringa, proprio come se aveste scritto qualcosa tipo C<$x .= ''>. =back In ogni caso, E sicuro utilizzare C

o C

per impacchettare una stringa letterale, perchE Perl alloca semplicemente una variabile anonima. =head1 Ricette di Impacchettamento Ecco un po' di ricette preconfezionate (possibilmente) utili per C e C: # Converti indirizzi IP per le funzioni sui socket pack( "C4", split /\./, "123.4.5.6" ); # Conta i bit in un segmento di memoria (per esempio un vettore select) unpack( '%32b*', $mask ); # Determina il tipo di finale del vostro sistema $a_finale_piccolo = unpack( 'c', pack( 's', 1 ) ); $a_finale_granden = unpack( 'xc', pack( 's', 1 ) ); # Determina il numero di bit in un intero nativo $numero_bit = unpack( '%32I!', ~0 ); # Prepara il parametro per la chiamata di sistema nanosleep my $specifica_temporale = pack( 'L!L!', $secondi, $nanosecondi ); Per una semplice stampata della memoria spacchettiamo alcuni ottetti in un numero corrispondente di coppie di cifre esadecimali, ed utilizziamo C per gestire la spaziatura tradizionale - 16 ottetti per riga: my $i; print map( ++$i % 16 ? "$_ " : "$_\n", unpack( 'H2' x length( $mem ), $mem ) ), length( $mem ) % 16 ? "\n" : ''; =head1 Sezione AmenitE # Estraiamo cifre dal nulla... print unpack( 'C', pack( 'x' ) ), unpack( '%B*', pack( 'A' ) ), unpack( 'H', pack( 'A' ) ), unpack( 'A', unpack( 'C', pack( 'A' ) ) ), "\n"; # Eccone una utile per la strada ;-) my $consiglio = pack( 'tutto quel che puoi nel furgoncino' ); =head1 Autori Simon Cozens e Wolfgang Laun. =head1 TRADUZIONE =head2 Versione La versione su cui si basa questa traduzione E ottenibile con: perl -MPOD2::IT -e print_pod perlpacktut Per maggiori informazioni sul progetto di traduzione in italiano si veda L . =head2 Traduttore Traduzione a cura di Flavio Poletti. =head2 Revisore Revisione a cura di dree. =cut