index | project | pods | responsabili |
NOMEperlpacktut - tutorial su
DESCRIZIONE
Il Principio BaseLa 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 In Perl, non potete accedere la memoria così a casaccio, ma la
conversione di struttura e di rappresentazione fornita da Perché, 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 è obbligatoria o vi porta qualche beneficio in termini di
elaborazione. Un'altra ragione può essere il passaggio di dati ad
una chiamata di sistema che non è disponibile come funzione Perl:
Per vedere come funziona l'impacchettamento (o lo spacchettamento),
cominciamo con un modello semplice dove la conversione è basilare:
tra il contenuto di una sequenza di ottetti ed una stringa di
caratteri esadecimali. Utilizziamo 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
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"; Poiché forniamo a
Impacchettare TestoSupponiamo 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 while (<>) { my $data = substr($_, 0, 11); my $descr = substr($_, 12, 27); my $entrate = substr($_, 40, 7); my $uscite = substr($_, 52, 7); ... } Non è proprio da ridere, eh? Infatti, è peggio di quanto possa sembrare: chi ha la vista aguzza può aver notato che il primo campo è ampio solo 10 caratteri, e che l'errore si è propagato attraverso gli altri valori - che dobbiamo contare a mano. Quindi, risulta soggetto ad errore così 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. while (<>) { my($data, $descr, $entrate, $uscite) = unpack("A10xA27xA7A*", $_); ... } Sembra già 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 - è larga 10 caratteri. ``Carattere'',
in gergo my($data) = unpack("A10", $_); OK, che viene dopo? Tra la data e la descrizione c'è una colonna vuota;
vogliamo saltarla. Il modello 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
Da qui, mettendo tutto insieme: my($data,$descriz,$entrate,$uscite) = unpack("A10xA27xA7xA*", $_); Ecco infine che i nostri dati sono interpretati. Suppongo che ciò 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'è 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, è un inizio, ma che è successo agli spazi? Abbiamo messo x A null byte. [Un ottetto nullo, NdT] Urgh. Nessuna meraviglia che non abbia funzionato. C'è 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 è di espandere la larghezza
dei campi. Il formato print pack("A11 A28 A8 A*", $data, "Totali", $tot_entrate, $tot_uscite); (Osservate che potete inserire spazi nel modello per renderlo più 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 già meglio, ma abbiamo ancora l'ultima colonna che va spostata un
po' più in là. C'è un modo semplice per risolvere questa
situazione: sfortunatamente, non possiamo chiedere a $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
Impacchettare NumeriCon i dati testuali abbiamo finito. Arriviamo allora alla ``ciccia'' vera,
dove
InteriImpacchettare ed estrarre numeri implica una conversione verso, e da, una qualche rappresentazione binaria specifica. Lasciando perdere i numeri a virgola mobile per il momento, le proprietà veramente importanti di qualunque rappresentazione del genere sono:
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 è una stringa, che ora contiene due ottetti. Se
stampate tale stringa (il che, in generale, non è consigliabile) potreste
vedere my( $s ) = unpack( 's', $ps ); Tutto ciò vale per tutti i codici di modello numerici. Non vi aspettate
miracoli, comunque: se il valore impacchettato eccede la capacità
in ottetti assegnata, i bit più significativi sono scartati senza
colpo ferire, ed 16 bit non vi porteranno molto lontano con gli interi, ma ci sono sempre
Ciascuno dei codici per interi 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
Spacchettare un Frame dello Stack[lo Stack è 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 stack frame 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 [little-endian, NdT], e che
questo è il motivo per cui l'ottetto di ordine basso è immagazzinato
all'indirizzo più basso. Per spacchettare un intero short (con segno)
di tal fatta dovremo utilizzare il codice my( $ip, $cs, $flags, $ax, $bx, $cd, $dx, $si, $di, $bp, $ds, $es ) = unpack( 'v12', $frame ); In alternativa, avremmo potuto utilizzare 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 short, tornare indietro un po', e poi spacchettare 2 ottetti.
Visto che Perl è carino, mette a disposizione il codice
di modello 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 può essere evitata - leggete!) Ci siamo presi la briga di costruire il modello in modo che corrisponda
al contenuto del nostro frame buffer. Altrimenti o avremmo ottenuto
valori indefiniti, o
Come Mangiare un Uovo su una ReteIl codice di impacchettamento per il finale grande [big-endian, NdT]
(ottetto di ordine alto all'indirizzo più basso) è my $buf = pack( 'N', length( $msg ) ) . $msg; o persino: my $buf = pack( 'NA*', length( $msg ), $msg ); e passare
Numeri a Virgola MobilePer impacchettare numeri in virgola mobile dovete scegliere fra i
codici
Modelli Esotici
Stringhe di BitI bit sono gli atomi del mondo della memoria. L'accesso ai bit
individuali potrebbe essere necessario o come ultima spiaggia o
perché è il modo più conveniente di trattare i vostri dati.
L'impacchettamento (o lo spacchettamento) in stringhe di bit
converte fra stringhe che contengono una serie di caratteri
7 6 5 4 3 2 1 0 +-----------------+ | 1 0 0 0 1 1 0 0 | +-----------------+ MSB LSB [MSB sta per Most Significant Bit, ossia il bit più significativo, quello di peso più alto; analogamente, LSB sta per Least Significant Bit, 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 più 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 è possibile impacchettare o spacchettare campi di bit - solo
ottetti interi. 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 può essere
fatta con il modello di spacchettamento ($carry, undef, $parity, undef, $auxcarry, undef, $zero, $sign, $trace, $interrupt, $direction, $overflow) = split( //, unpack( 'b16', $status ) ); Avremmo anche potuto utilizzare un modello di spacchettamento
UuencodeUn altro strano tipo nell'alfabeto dei modelli di impacchettamento
e spacchettamento è my $uubuf = pack( 'u', $bindat ); Un indicatore di ripetizione dopo
Fare le SommeUn codice di modello persino più strano è my $buf = pack( 'iii', 100, 20, 3 ); print unpack( '%32i3', $buf ), "\n"; # stampa 123 Per i valori stringa, print unpack( '%32A*', "\x01\x10" ), "\n"; # prints 17 Sebbene la documentazione del codice In connessione con my $conteggiobit = unpack( '%32b*', $mask ); Ed un bit di parità può essere determinato come segue: my $parita = unpack( '%1b*', $mask );
UnicodeUnicode è un insieme di caratteri che può rappresentare la maggior parte dei caratteri nella maggior parte delle lingue al mondo, avendo spazio per più 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 varietà di insiemi di simboli come i segni per le monete, Zapf Dingbat e Braille. (Potreste sempre fare una visita su www.unicode.org 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 più che raddoppiare i requisiti di immagazzinamento di testi scritti con alfabeti latini. La codifica UTF-8 evita questo spreco immagazzinando i caratteri più comuni (da un punto di vista del mondo occidentale) in un singolo ottetto, mentre i più rari sono immagazzinati in tre o più ottetti. Dunque, che cosa ha a che fare tutto ciò con $UTF8{Euro} = pack( 'U', 0x20AC ); Andando a vedere $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 );
Un'altra Codifica Binaria PortabileIl codice di impacchettamento my $berbuf = pack( 'w*', 1, 128, 128+1, 128*128+127 ); Una stampa esadecimale di
Raggruppamento di ModelliPrima di Perl 5.8, la ripetizione dei modelli doveva esser fatta
con l'utilizzo dell'operatore unpack( 'v2 (vXXCC)5 v5', $frame ) Diamo un'occhiata più da vicino a questa opportunità. Cominciamo con l'equivalente di join( '', map( substr( $_, 0, 1 ), @str ) ) che restituisce una stringa composta del primo carattere di ciascuna
stringa. Utilizzando pack( '(A)'.@str, @str ) o, viso che il contatore di ripetizione pack( '(A)*', @str ) (Osservate che il modello Per impacchettare le date immagazzinandole come triplette
(giorno, mese, anno) da un array $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
$s = pack( '(A)*', unpack( '(xAXXAx)*', $s ) ); Possiamo anche utilizzare $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 );
Lunghezze e larghezze
Lunghezza delle stringheNella sezione precedente abbiamo visto un messaggio di rete costruito mettendo la lunghezza del messaggio binario come prefisso al messaggio vero e proprio. Vi capiterà di trovare che l'impacchettamento di una lunghezza seguita da quel certo numero di ottetti di dati è una ricetta utilizzata comunemente, perché 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 Short Message (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 può essere effettuato con lo stesso modello: ( $src, $dst, $len, $sm ) = unpack( 'Z*Z*CA*', $msg ); C'è una sottile trappola che si aggira: aggiungere un altro campo
dopo lo Short Message (nella variabile # 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 # 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 ( Il codice di impacchettamento che precede # 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 );
# 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
Modelli DinamiciFino ad ora, abbiamo visto modelli composti di soli letterali. Se la lista
degli elementi di impacchettamento non ha una lunghezza prefissata, c'è
bisogno di un'espressione che costruisca il modello (in tutti quei casi
in cui, per qualche motivo, non sia possibile utilizzare 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'è la
chiamata a Per l'operazione inversa, dovremo determinare il numero di elementi nel
buffer prima di lasciare che my $n = $env =~ tr/\0// - 1; my %env = map( split( /=/, $_ ), unpack( "(Z*)$n", $env ) ); La chiamata a
Contare le ripetizioniPiuttosto 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, poiché il numero di ripetizioni
può essere estratto con il codice my %env = unpack( 'S/(S/A* S/A*)', $env ); Osservate che questo è uno dei rari casi nei quali non potete utilizzare
lo stesso modello per
Impacchettare e Spacchettare Strutture CNelle 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 perciò già sapete tutto quello che c'è da sapere. Spiacenti, non è così: leggete e capirete.
La Fossa dell'AllineamentoIn un confronto fra requisiti velocità e memoria la bilancia è stata spostata verso un'esecuzione più rapida. Cià 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 più 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 darà questo beneficio di velocità inserendo ottetti extra nelle strutture. Se non attraversate la banchina del C non vi darà 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 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
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 è dove colpisce la prima stranezza: i modelli di L'ovvia domanda: ``Perché Perl non compensa da solo questi buchi?'' merita
una risposta. Una buona ragione è che il compilatore C potrebbe fornire
delle estensioni (non ANSI) che consentono tutte le varietà di
controllo sul modo in cui le strutture vengono allineate, persino a
livello di singolo campo della struttura. E, se non fosse già abbastanza,
esiste una cosa insidiosa chiamata OK, via il dente, via il dolore. Ecco un modo per sistemare
l'allineamento inserendo i codici di modello my $gappy = pack( 'cxs cxxx l!', $c1, $s, $c2, $l ); Osservate il Contare gli ottetti e star dietro agli allineamenti in strutture corpose è 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 <stdio.h> #include <stddef.h> 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 può essere utilizzata come modello nella
chiamata a my $groviera = pack( '@0c @2s! @4c @8l!', $c1, $s, $c2, $l ); Cribbio, un altro codice di modello - come se non ne avessimo abbastanza.
Ma Né l'utilizzo degli scostamenti né l'aggiunta di 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 portabilità. Piuttosto che my $groviera = pack( 'c x![s] s c x![l!] l!', $c1, $s, $c2, $l );
Allineamento, seconda ripresaHo 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 è richiesto né
prima del primo campo # something goes wrong here: pack( 's!a' x @buffer, map{ ( $_->{conteggio}, $_->{glifo} ) } @buffer ); Questo impacchetta pack( 's!ax' x @buffer, map{ ( $_->{conteggio}, $_->{glifo} ) } @buffer );
Alignment, terza ripresaEd 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
può 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
Puntatori per Come UtilizzarliIl 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
Il codice di modello # alloca un po' di spazio ed impacchetta un puntatore ad esso my $memoria = "\x00" x $dimensione; my $memptr = pack( 'P', $memoria ); Aspettate: 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 può essere preso come legge universale. Ora che abbiamo questo puntatore la prossima domanda è: 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 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 è né un esempio di semplicità né un paragone di
portabilità, 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 Come funziona my $mem = "abcdefghijklmn"; print unpack( 'P5', pack( 'P', $mem ) ); # stampa "abcde" Di conseguenza, Ora che abbiamo visto 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 è conseguenza della lunghezza della stringa, un numero dopo il
codice di impacchettamento L'utilizzo di
In ogni caso, è sicuro utilizzare
Ricette di ImpacchettamentoEcco un po' di ricette preconfezionate (possibilmente) utili per # 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
my $i; print map( ++$i % 16 ? "$_ " : "$_\n", unpack( 'H2' x length( $mem ), $mem ) ), length( $mem ) % 16 ? "\n" : '';
Sezione Amenità# 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' );
AutoriSimon Cozens e Wolfgang Laun.
TRADUZIONE
VersioneLa versione su cui si basa questa traduzione è ottenibile con: perl -MPOD2::IT -e print_pod perlpacktut Per maggiori informazioni sul progetto di traduzione in italiano si veda http://pod2it.sourceforge.net/ .
TraduttoreTraduzione a cura di Flavio Poletti.
RevisoreRevisione a cura di dree. Mon Jun 11 22:02:18 2012 |