Aceasta este comanda perlpacktut care poate fi rulată în furnizorul de găzduire gratuit OnWorks folosind una dintre multiplele noastre stații de lucru online gratuite, cum ar fi Ubuntu Online, Fedora Online, emulator online Windows sau emulator online MAC OS
PROGRAM:
NUME
perlpacktut - tutorial despre „împachetați” și „despachetați”
DESCRIERE
„pack” și „unpack” sunt două funcții pentru transformarea datelor în funcție de un definit de utilizator
șablon, între modul în care Perl stochează valori și o reprezentare bine definită
așa cum ar putea fi necesar în mediul unui program Perl. Din păcate, sunt și ei doi
dintre funcțiile cele mai greșit înțelese și cel mai adesea trecute cu vederea pe care le oferă Perl. Acest
tutorialul le va demistifica pentru tine.
pachet de bază Principiu
Majoritatea limbajelor de programare nu adăpostesc memoria în care sunt stocate variabilele. În C, pentru
De exemplu, puteți lua adresa unei variabile și operatorul „sizeof” vă spune
câți octeți sunt alocați variabilei. Folosind adresa și dimensiunea, puteți
accesați spațiul de stocare la conținutul inimii dvs.
În Perl, pur și simplu nu poți accesa memoria la întâmplare, ci cea structurală și reprezentativă
conversia oferită de „pack” și „unpack” este o alternativă excelentă. Haita"
funcția convertește valorile într-o secvență de octeți care conține reprezentări conform a
specificație dată, așa-numitul argument „șablon”. „despachetați” este procesul invers,
derivând unele valori din conținutul unui șir de octeți. (Fii atent, totuși, că
nu tot ceea ce a fost împachetat împreună poate fi despachetat cu grijă - o experiență foarte comună ca
este posibil ca călătorii experimentați să confirme.)
De ce, vă puteți întreba, ați avea nevoie de o bucată de memorie care să conțină niște valori în binar
reprezentare? Un motiv bun este intrarea și ieșirea accesând un fișier, un dispozitiv sau un
conexiune la rețea, prin care această reprezentare binară fie este forțată asupra dvs., fie va fi
vă oferă un anumit beneficiu în procesare. O altă cauză este transmiterea datelor către un apel de sistem
care nu este disponibilă ca funcție Perl: „syscall” vă cere să furnizați parametri
stocate în modul în care se întâmplă într-un program C. Procesare uniformă a textului (după cum se arată în următorul
secțiunea) poate fi simplificată prin utilizarea judicioasă a acestor două funcții.
Pentru a vedea cum funcționează (dez)ambalarea, vom începe cu un cod șablon simplu unde conversia
este în treapta joasă: între conținutul unei secvențe de octeți și un șir de hexazecimale
cifre. Să folosim „unpack”, deoarece este probabil să vă amintească de un program de descărcare sau de unele
Ultimul mesaj disperat programele nefericite sunt obișnuite să vă arunce înainte de a expira
în albastrul sălbatic de acolo. Presupunând că variabila $mem deține o secvență de octeți care
am dori să inspectăm fără să presupunem nimic despre semnificația lui, putem scrie
my( $hex ) = unpack( 'H*', $mem );
imprimă „$hex\n”;
după care am putea vedea ceva de genul acesta, cu fiecare pereche de cifre hexadecimale corespunzătoare
un octet:
41204d414e204120504c414e20412043414e414c2050414e414d41
Ce era în această bucată de memorie? Numere, caractere sau un amestec al ambelor? Asumand
suntem pe un computer unde este folosită codificare ASCII (sau ceva similar): valori hexazecimale în
intervalul 0x40 - 0x5A indică o literă mare, iar 0x20 codifică un spațiu. Deci am putea
să presupunem că este o bucată de text, pe care unii sunt capabili să o citească ca un tabloid; dar alții o vor face
trebuie să pună mâna pe o masă ASCII și să retrăiesc acel sentiment de elev. Nici nu-i pasă
mult despre modul în care să citiți acest lucru, observăm că „despachetați” cu codul șablonului „H”
convertește conținutul unei secvențe de octeți în notația hexazecimală obișnuită.
Deoarece „o secvență de” este o indicație destul de vagă a cantității, „H” a fost definit ca
convertiți doar o singură cifră hexazecimală, cu excepția cazului în care este urmată de o numărare repetată. Un
asteriscul pentru numărarea repetată înseamnă a folosi tot ceea ce rămâne.
Operația inversă - împachetarea conținutului de octeți dintr-un șir de cifre hexazecimale - este
la fel de usor de scris. De exemplu:
my $s = pack( 'H2' x 10, 30..39 );
imprimă „$s\n”;
Deoarece introducem o listă de zece șiruri hexazecimale din 2 cifre pentru a „împacheta”, șablonul de pachet
ar trebui să conțină zece coduri de pachet. Dacă aceasta este rulată pe un computer cu codare de caractere ASCII,
va imprima 0123456789.
Ambalare Text
Să presupunem că trebuie să citiți într-un fișier de date ca acesta:
Data |Descriere | Venituri|Cheltuieli
01 Zed's Camel Emporium 24
01 Spray antipurici 28
01 Plimbare cu camila catre turisti 29
Cum o facem? S-ar putea să vă gândiți mai întâi să folosiți „split”; cu toate acestea, din moment ce „despărțirea” se prăbușește
câmpuri goale, nu veți ști niciodată dacă o înregistrare a fost venit sau cheltuieli. Hopa! Bine,
ați putea folosi întotdeauna „substr”:
în timp ce (<>) {
my $date = substr($_, 0, 11);
my $desc = substr($_, 12, 27);
venitul meu $ = substr($_, 40, 7);
my $expend = substr($_, 52, 7);
...
}
Nu este chiar un butoi de râsete, nu-i așa? De fapt, este mai rău decât pare; cel
eagle-eyed poate observa că primul câmp ar trebui să aibă o lățime de numai 10 caractere, iar
eroarea s-a propagat direct prin celelalte numere - pe care a trebuit să le numărăm manual.
Deci este predispus la erori, precum și îngrozitor de neprietenos.
Sau poate am putea folosi expresii regulate:
în timp ce (<>) {
my($data, $desc, $venit, $cheltuiala) =
m|(\d\d/\d\d/\d{4}) (.{27}) (.{7})(.*)|;
...
}
Urgh. Ei bine, e puțin mai bine, dar... ei bine, ai vrea să menții asta?
Hei, nu ar trebui Perl să ușureze astfel de lucruri? Ei bine, da, dacă folosești
instrumentele potrivite. „ambalați” și „despachetați” sunt concepute pentru a vă ajuta atunci când aveți de-a face cu probleme fixe.
date de lățime ca cele de mai sus. Să aruncăm o privire la o soluție cu „unpack”:
în timp ce (<>) {
my($data, $desc, $income, $expend) = unpack("A10xA27xA7A*", $_);
...
}
Asta pare un pic mai frumos; dar trebuie să demontam acel șablon ciudat. Unde am tras
asta din?
OK, să aruncăm o privire la unele dintre datele noastre din nou; de fapt, vom include anteturile și a
riglă la îndemână, astfel încât să putem urmări unde ne aflăm.
1 2 3 4 5
1234567890123456789012345678901234567890123456789012345678
Data |Descriere | Venituri|Cheltuieli
01 Spray antipurici 28
01 Plimbare cu camila catre turisti 29
Din aceasta, putem vedea că coloana datei se întinde de la coloana 1 la coloana 10 - zece
caractere late. „Pachetul”-ese pentru „personaj” este „A”, iar zece dintre ele sunt „A10”. Astfel, dacă
am vrut doar să extragem datele, am putea spune asta:
my($date) = unpack("A10", $_);
OK, ce urmează? Între dată și descriere este o coloană goală; vrem să sărim
Peste aia. Șablonul „x” înseamnă „sări înainte”, așa că vrem unul dintre acestea. În continuare, avem
un alt lot de caractere, de la 12 la 38. Adică încă 27 de caractere, de unde „A27”. (Nu
faceți eroarea stâlpului de gard - există 27 de caractere între 12 și 38, nu 26. Numără-le!)
Acum sărim peste alt caracter și alegem următoarele 7 caractere:
my($date,$description,$income) = unpack("A10xA27xA7", $_);
Acum vine partea inteligentă. Linii din registrul nostru care sunt doar venituri și nu cheltuieli
s-ar putea termina la coloana 46. Prin urmare, nu vrem să spunem modelului nostru de „despachetare” că noi nevoie la
găsiți alte 12 caractere; vom spune doar „dacă a mai rămas ceva, ia-l”. Ca tine
s-ar putea ghici din expresiile regulate, asta înseamnă „*”: „folosește totul
rămas".
· Fiți avertizat, totuși, că, spre deosebire de expresiile obișnuite, dacă șablonul „despachetați” nu
potriviți cu datele primite, Perl va țipa și va muri.
Prin urmare, punând totul împreună:
meu ($data, $descriere, $venit, $cheltuială) =
unpack("A10xA27xA7xA*", $_);
Acum, acestea sunt datele noastre analizate. Presupun că ceea ce am putea dori să facem acum este să ne însumăm veniturile
și cheltuieli și adăugați o altă linie la sfârșitul registrului nostru - în același format -
spunând cât am adus și cât am cheltuit:
în timp ce (<>) {
meu ($data, $desc, $venit, $cheltuiala) =
unpack("A10xA27xA7xA*", $_);
$tot_income += $venit;
$tot_expend += $expend;
}
$tot_income = sprintf("%.2f", $tot_income); # Introduceți-le
$tot_expend = sprintf("%.2f", $tot_expend); # format „financiar”.
$date = POSIX::strftime("%m/%d/%Y", ora locala);
# OK hai să mergem:
pachet de imprimare ("A10xA27xA7xA*", $date, "Totale",
$tot_income, $tot_expend);
Oh, hmm. Asta nu prea a funcționat. Să vedem ce s-a întâmplat:
01 Zed's Camel Emporium 24
01 Spray antipurici 28
01 Plimbare cu camila catre turisti 29
03Totale 23
OK, este un început, dar ce s-a întâmplat cu spațiile? Am pus "x", nu? Nu ar trebui
sari inainte? Să ne uităm la ce spune „pachet” în perlfunc:
x Un octet nul.
Urgh. Nu-i de mirare. Există o mare diferență între „un octet nul”, caracterul zero și „a
spaţiu”, caracterul 32. Perl a pus ceva între dată şi descriere – dar
din pacate, nu putem vedea!
Ceea ce trebuie de fapt să facem este să extindem lățimea câmpurilor. Formatul „A” completează orice
caractere inexistente cu spații, astfel încât să putem folosi spațiile suplimentare pentru a ne alinia
câmpuri, astfel:
pachet de tipărire ("A11 A28 A8 A*", $date, "Totale",
$tot_income, $tot_expend);
(Rețineți că puteți pune spații în șablon pentru a-l face mai lizibil, dar nu o fac
traduceți în spații în ieșire.) Iată ce am obținut de data aceasta:
01 Zed's Camel Emporium 24
01 Spray antipurici 28
01 Plimbare cu camila catre turisti 29
03 Totale 23 2001
E puțin mai bine, dar mai avem ultima coloană care trebuie mutată mai departe
peste. Există o modalitate ușoară de a remedia acest lucru: din păcate, nu putem face „pack” la dreapta-
justifică câmpurile noastre, dar putem obține „sprintf” să o facă:
$tot_income = sprintf("%.2f", $tot_income);
$tot_expend = sprintf("%12.2f", $tot_expend);
$date = POSIX::strftime("%m/%d/%Y", ora locala);
pachet de tipărire ("A11 A28 A8 A*", $date, "Totale",
$tot_income, $tot_expend);
De data aceasta primim răspunsul corect:
01 Spray antipurici 28
01 Plimbare cu camila catre turisti 29
03 Totale 23 2001
Deci, așa consumăm și producem date cu lățime fixă. Să recapitulăm ceea ce am văzut
„ambalați” și „despachetați” până acum:
· Utilizați „pachet” pentru a trece de la mai multe date la o versiune cu lățime fixă; folosiți „despachetați”
pentru a transforma un șir cu format fix cu lățime în mai multe date.
· Formatul pachet „A” înseamnă „orice caracter”; dacă „împachetezi” și ai rămas fără
lucruri de împachetat, „împachetați” va umple restul cu spații.
· „x” înseamnă „sări peste un octet” când „despachetează”; atunci când „ambalați”, înseamnă „introduceți un nul
octet" - probabil că nu asta vrei să spui dacă ai de-a face cu text simplu.
· Puteți urma formatele cu numere pentru a spune câte caractere ar trebui să fie afectate
după acel format: „A12” înseamnă „luați 12 caractere”; „x6” înseamnă „săriți 6 octeți” sau
„caracter 0, 6 ori”.
· În loc de un număr, puteți folosi „*” pentru a însemna „consumați tot ce a mai rămas”.
avertizare: când împachetați mai multe date, „*” înseamnă doar „consumați tot
date curente". Adică
pachet("A*A*", $unu, $două)
împachetează toți $one în primul „A*” și apoi toți $XNUMX în al doilea. Acesta este un
principiu general: fiecare caracter de format corespunde unei date care trebuie să fie
"bătătorit.
Ambalare Numere
Atât pentru datele textuale. Să trecem la chestiile cu carne care „împachetează” și „despachetează” sunt cele mai bune
la: manipularea formatelor binare pentru numere. Desigur, nu există un singur format binar
- viața ar fi prea simplă - dar Perl va face toată munca capricioasă pentru tine.
Întregi
Ambalarea și despachetarea numerelor implică conversia către și de la unele specific binar
reprezentare. Lăsând numerele în virgulă mobilă deoparte pentru moment, principalul
Proprietățile oricărei astfel de reprezentări sunt:
· numărul de octeți utilizați pentru stocarea întregului,
· dacă conținutul este interpretat ca un număr semnat sau nesemnat,
· ordonarea octetilor: dacă primul octet este cel mai mic sau cel mai semnificativ octet (sau:
little-endian sau, respectiv, big-endian).
Deci, de exemplu, pentru a împacheta 20302 într-un număr întreg semnat de 16 biți în computerul dvs.
reprezentare pe care o scrii
my $ps = pack('s', 20302);
Din nou, rezultatul este un șir, care conține acum 2 octeți. Dacă imprimați acest șir (adică,
în general, nu este recomandat), este posibil să vedeți „ON” sau „NU” (în funcție de octetul sistemului dumneavoastră
ordonare) - sau ceva complet diferit dacă computerul dvs. nu folosește caracterul ASCII
codificare. Despachetarea $ps cu același șablon returnează valoarea inițială întreagă:
my( $s ) = unpack( 's', $ps );
Acest lucru este valabil pentru toate codurile de șablon numeric. Dar nu vă așteptați la miracole: dacă sunt împachetate
valoarea depășește capacitatea de octet alocată, biții de ordin înalt sunt eliminați în tăcere și
despachetarea cu siguranță nu le va putea scoate înapoi dintr-o pălărie magică. Și, când împachetați
folosind un cod șablon semnat, cum ar fi „s”, o valoare în exces poate avea ca rezultat bitul de semn
se setează, iar despachetarea acestuia va returna inteligent o valoare negativă.
16 biți nu te vor duce prea departe cu numere întregi, dar există „l” și „L” pentru semnate și
numere întregi nesemnate pe 32 de biți. Și dacă acest lucru nu este suficient și sistemul dvs. acceptă 64 de biți
numere întregi puteți împinge limitele mult mai aproape de infinit cu codurile de pachet „q” și „Q”. A
o excepție notabilă este oferită de codurile de pachet „i” și „I” pentru numerele întregi semnate și nesemnate
din varietatea „personalizat local”: un astfel de număr întreg va ocupa la fel de mulți octeți ca un C local
compilatorul returnează „sizeof(int)”, dar va folosi at cel mai puțin 32 de biți.
Fiecare dintre codurile pachetului întreg „sSlLqQ” are ca rezultat un număr fix de octeți, indiferent
unde executați programul dvs. Acest lucru poate fi util pentru unele aplicații, dar nu este
oferă o modalitate portabilă de a trece structuri de date între programele Perl și C (legate la
se întâmplă când apelați extensiile XS sau funcția Perl „syscall”) sau când citiți sau
scrie fișiere binare. Ceea ce veți avea nevoie în acest caz sunt coduri șablon care depind de ce anume
compilatorul dvs. local C se compilează atunci când codați „short” sau „unsigned long”, de exemplu.
Aceste coduri și lungimile de octeți corespunzătoare sunt prezentate în tabelul de mai jos. Din momentul în care
Standardul C lasă multă libertate în ceea ce privește dimensiunile relative ale acestor tipuri de date,
valorile reale pot varia și de aceea valorile sunt date ca expresii în C și Perl.
(Dacă doriți să utilizați valorile din %Config în programul dvs., trebuie să îl importați cu „use
Configurare".)
Lungimea octetului semnat fără semn în lungime de octet C în Perl
s! S! sizeof(short) $Config{shortsize}
eu! eu! sizeof(int) $Config{intsize}
eu! L! sizeof(lung) $Config{longsize}
q! Q! sizeof(long long) $Config{longlongsize}
"i!" și eu!" codurile nu sunt diferite de „i” și „I”; sunt tolerate pentru
de dragul completitudinii.
Despachetarea a Stivui Inrameaza-le
Solicitarea unei anumite ordini de octeți poate fi necesară atunci când lucrați cu date binare
care provine dintr-o arhitectură specifică, în timp ce programul dvs. ar putea rula complet
sistem diferit. De exemplu, să presupunem că aveți 24 de octeți care conțin un cadru de stivă
se întâmplă pe un Intel 8086:
+---------+ +----+----+ +---------+
TOS: | IP | TOS+4:| FL | FH | DRAPEURI TOS+14:| SI |
+---------+ +----+----+ +---------+
| CS | | AL | AH | AX | DI |
+---------+ +----+----+ +---------+
| BL | BH | BX | BP |
+----+----+ +---------+
| CL | CH | CX | DS |
+----+----+ +---------+
| DL | DH | DX | ES |
+----+----+ +---------+
În primul rând, observăm că acest procesor de 16 biți onorat în timp folosește ordinea little-endian și de aceea
octetul de ordin mic este stocat la adresa inferioară. Pentru a despacheta un astfel de scurt (nesemnat) vom face
trebuie să folosească codul „v”. O numărare repetată despachetează toate cele 12 scurte:
my( $ip, $cs, $flags, $ax, $bx, $cd, $dx, $si, $di, $bp, $ds, $es ) =
unpack('v12', $frame);
Alternativ, am fi putut folosi „C” pentru a despacheta registrele de octeți accesibile individual
FL, FH, AL, AH etc.:
my( $fl, $fh, $al, $ah, $bl, $bh, $cl, $ch, $dl, $dh ) =
unpack( 'C10', substr( $cadru, 4, 10 ) );
Ar fi bine dacă am putea face asta dintr-o singură lovitură: despachetați un scurt, faceți înapoi puțin,
și apoi despachetați 2 octeți. De când Perl is frumos, oferă codul șablonului „X” pentru a face backup
un octet. Punând toate acestea cap la cap, acum putem scrie:
meu( $ip, $cs,
$steaguri,$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 );
(Construcția stângace a șablonului poate fi evitată - citiți mai departe!)
Ne-am străduit să construim șablonul astfel încât să se potrivească cu conținutul nostru
cadru tampon. În caz contrar, fie am obține valori nedefinite, fie „unpack” nu ar putea despacheta
toate. Dacă „pachet” rămâne fără articole, va furniza șiruri nule (care sunt forțate în
zero ori de câte ori codul pachetului spune acest lucru).
Cum la Mânca an Ou on a Net
Codul pachetului pentru big-endian (octet de ordin înalt la adresa cea mai mică) este „n” pentru 16 biți și
„N” pentru numere întregi pe 32 de biți. Folosești aceste coduri dacă știi că datele tale provin de la a
arhitectură conformă, dar, în mod surprinzător, ar trebui să utilizați și aceste coduri de pachet dacă
faci schimb de date binare, prin rețea, cu un sistem pe care îl cunoști lângă
nimic despre. Motivul simplu este că această ordine a fost aleasă drept reţea comandă,
și toate programele care se tem de standarde ar trebui să urmeze această convenție. (Acesta este, desigur, un
sprijin sever pentru una dintre partidele liliputiene și poate influența bine politica
dezvoltare acolo.) Deci, dacă protocolul se așteaptă să trimiteți un mesaj prin trimiterea
lungimea mai întâi, urmată de atât de mulți octeți, ați putea scrie:
my $buf = pack( 'N', lungime ( $msg ) ) . $msg;
sau chiar:
my $buf = pack( 'NA*', length( $msg ), $msg );
și treceți $buf rutinei dvs. de trimitere. Unele protocoale cer ca numărarea să includă
lungimea numărării în sine: apoi adăugați doar 4 la lungimea datelor. (Dar asigurați-vă că citiți
„Lungimi și lățimi” înainte de a codifica cu adevărat acest lucru!)
Ordinea octetilor modificatori
În secțiunile anterioare am învățat cum să folosim „n”, „N”, „v” și „V” pentru a împacheta și a despacheta
numere întregi cu ordinea octetilor endian mari sau mici. Deși acest lucru este frumos, este încă mai degrabă
limitat, deoarece omite toate tipurile de numere întregi cu semn, precum și numere întregi pe 64 de biți. Pentru
exemplu, dacă doriți să despachetați o secvență de numere întregi big-endian pe 16 biți semnate într-un
în mod independent de platformă, ar trebui să scrieți:
my @data = despachetează 's*', pack 'S*', despachetează 'n*', $buf;
Asta e urât. Începând cu Perl 5.9.2, există o modalitate mult mai plăcută de a-ți exprima dorința de a
anumită ordine de octeți: modificatorii „>” și „<”. „>” este modificatorul big-endian, în timp ce „<”
este modificatorul little-endian. Folosindu-le, am putea rescrie codul de mai sus ca:
my @data = despachetează 's>*', $buf;
După cum puteți vedea, „capătul mare” al săgeții atinge „s”, ceea ce este un mod frumos de a
rețineți că „>” este modificatorul big-endian. Același lucru funcționează, evident, pentru „<”, unde
„Sfârșitul mic” atinge codul.
Veți găsi probabil acești modificatori și mai folositori dacă aveți de-a face cu big- sau
structuri C little-endian. Asigurați-vă că citiți „Ambalarea și dezambalarea structurilor C” pentru mai multe
pe asta.
plutitor punct Numere
Pentru împachetarea numerelor în virgulă mobilă aveți posibilitatea de a alege între codurile de pachet „f”, „d”,
„F” și „D”. „f” și „d” se împachetează în (sau despachetează din) precizie simplă sau precizie dublă
reprezentare așa cum este furnizată de sistemul dvs. Dacă sistemul dumneavoastră îl acceptă, „D” poate fi
folosit pentru a împacheta și a despacheta valori ("long double"), care pot oferi chiar mai multă rezoluție decât
„f” sau „d”. notițe acea acolo sunt diferit lung dubla formate.
„F” include un „NV”, care este tipul în virgulă mobilă utilizat de Perl intern.
Nu există o reprezentare de rețea pentru real, așa că dacă doriți să trimiteți
numere reale dincolo de granițele computerului, ar fi bine să rămâneți la reprezentarea textului,
posibil folosind formatul hexazecimal flotant (evitând pierderea conversiei zecimale), cu excepția cazului în care
ești absolut sigur ce se află la celălalt capăt al firului. Pentru chiar mai mult
aventuros, puteți folosi și modificatorii de ordine a octetilor din secțiunea anterioară
coduri în virgulă mobilă.
Exotic Șabloane
Pic Corzi
Biții sunt atomii din lumea memoriei. Este posibil să fie necesar să fie utilizat accesul la biți individuali
fie ca ultimă soluție, fie pentru că este cea mai convenabilă modalitate de a vă gestiona datele. Pic
string (un)packing convertește între șiruri care conțin o serie de 0 și 1 caractere și
o secvență de octeți care conține fiecare un grup de 8 biți. Acest lucru este aproape la fel de simplu
sunete, cu excepția faptului că există două moduri în care conținutul unui octet poate fi scris ca bit
şir. Să aruncăm o privire la un octet adnotat:
7 6 5 4 3 2 1 0
+-----------------+
| 1 0 0 0 1 1 0 0 |
+-----------------+
MSB LSB
Mănâncă ouă din nou: Unii cred că acest lucru ar trebui scris ca șir
„10001100” adică începând cu bitul cel mai semnificativ, alții insistă pe „00110001”.
Ei bine, Perl nu este părtinitor, așa că de aceea avem coduri de șir de doi biți:
$byte = pack( 'B8', '10001100'); # începe cu MSB
$byte = pack( 'b8', '00110001'); # începe cu LSB
Nu este posibil să împachetați sau despachetați câmpuri de biți - doar octeți integrali. „pachet” întotdeauna
începe la următoarea limită de octeți și „se rotunjește” la următorul multiplu de 8 prin adăugarea zero
biți după cum este necesar. (Dacă doriți câmpuri de biți, există „vec” în perlfunc. Sau ați putea
implementați gestionarea câmpurilor de biți la nivel de șir de caractere, folosind split, substr și
concatenare pe șiruri de biți despachetate.)
Pentru a ilustra dezambalarea șirurilor de biți, vom descompune un registru de stare simplu (un „-”
reprezintă un bit „rezervat”):
+-----------------+-----------------+
| SZ - A - P - C | - - - - ODIT |
+-----------------+-----------------+
MSB LSB MSB LSB
Convertirea acestor doi octeți într-un șir se poate face cu șablonul de despachetare „b16”. La
obțineți valorile individuale de biți din șirul de biți pe care îl folosim „split” cu „gol”
model separator care disecă în caractere individuale. Valorile biților din
Pozițiile „rezervate” sunt pur și simplu atribuite lui „undef”, o notație convenabilă pentru „Nu
pasă unde se duce asta”.
($carry, undef, $parity, undef, $auxcarry, undef, $zero, $sign,
$urmă, $întrerupere, $direcție, $debordare) =
split( //, unpack( 'b16', $status) );
Am fi putut folosi un șablon de despachetare „b12” la fel de bine, deoarece ultimii 4 biți pot fi
ignorat oricum.
Uuencoding
Un alt om ciudat din alfabetul șablonului este „u”, care include un „șir codificat uu”.
("uu" este prescurtarea pentru Unix-to-Unix.) Sunt șanse să nu veți avea nevoie vreodată de această codificare
tehnică care a fost inventată pentru a depăși neajunsurile transmisiei de modă veche
medii care nu suportă altele decât date simple ASCII. Rețeta esențială este simplă:
Luați trei octeți sau 24 de biți. Împărțiți-le în 4 pachete de șase, adăugând un spațiu (0x20) la fiecare.
Repetați până când toate datele sunt amestecate. Îndoiți grupuri de 4 octeți în linii de cel mult
60 și garniți-le în față cu numărul inițial de octeți (incrementat cu 0x20) și un „\n”
la sfârșitul. - Bucătarul „pachet” vă va pregăti acest lucru, la minut, când selectați pachet
codul „u” din meniu:
my $uubuf = pack( 'u', $bindat );
Un număr de repetare după „u” stabilește numărul de octeți de introdus într-o linie codificată uuen, adică
maximul de 45 în mod implicit, dar ar putea fi setat la un multiplu întreg (mai mic).
Trei. „despachetați” pur și simplu ignoră numărul de repetări.
Face Sume
Un cod șablon și mai ciudat este „%”număr>. În primul rând, pentru că este folosit ca prefix pentru
alt cod șablon. În al doilea rând, pentru că nu poate fi folosit deloc în „pachet” și în al treilea rând,
în „unpack”, nu returnează datele așa cum sunt definite de codul șablonului pe care îl precede. In schimb
vă va oferi un număr întreg de număr biți care se calculează din valoarea datelor făcând
sume. Pentru codurile de despachetare numerice, nu se realizează nicio performanță mare:
my $buf = pack( 'iii', 100, 20, 3 );
print unpack( '%32i3', $buf ), "\n"; # printează 123
Pentru valorile șirului, „%” returnează suma valorilor octeților, scutindu-vă de problemele unei sume
buclă cu „substr” și „ord”:
print unpack( '%32A*', "\x01\x10" ), "\n"; # printuri 17
Deși codul „%” este documentat ca returnând o „sumă de control”: nu vă puneți încrederea
asemenea valori! Chiar și atunci când sunt aplicați la un număr mic de octeți, aceștia nu vor garanta a
distanță Hamming vizibilă.
În legătură cu „b” sau „B”, „%” adaugă pur și simplu biți, iar acest lucru poate fi folosit la
numărați eficient biții setați:
my $bitcount = unpack( '%32b*', $mask );
Și un bit de paritate uniformă poate fi determinat astfel:
my $evenparity = unpack( '%1b*', $mask );
Unicode
Unicode este un set de caractere care poate reprezenta majoritatea caracterelor din cele mai multe din lume
limbi, oferind spațiu pentru peste un milion de personaje diferite. Unicode 3.1 specifică
94,140 de caractere: caracterele latine de bază sunt alocate numerelor de la 0 la 127.
Latin-1 Suplimentul cu caractere care sunt folosite în mai multe limbi europene se află în
următorul interval, până la 255. După câteva extensii latine mai găsim seturile de caractere din
limbi care folosesc alfabete non-romane, intercalate cu o varietate de seturi de simboluri, cum ar fi
simboluri valutare, Zapf Dingbats sau Braille. (S-ar putea să doriți să vizitați
<http://www.unicode.org/> pentru a arunca o privire la unele dintre ele - preferatele mele personale sunt telugu
și Kannada.)
Seturile de caractere Unicode asociază caractere cu numere întregi. Codificarea acestor numere în
un număr egal de octeți ar dubla mai mult decât cerințele pentru stocarea textelor scrise
în alfabetul latin. Codificarea UTF-8 evită acest lucru prin stocarea celor mai comune (de la a
din punct de vedere occidental) caractere într-un singur octet în timp ce le codifică pe cele mai rare în trei
sau mai mulți octeți.
Perl folosește UTF-8, intern, pentru majoritatea șirurilor Unicode.
Deci ce legătură are asta cu „pachet”? Ei bine, dacă doriți să compuneți un șir Unicode
(care este codificat intern ca UTF-8), puteți face acest lucru folosind codul șablon „U”. Ca un
de exemplu, să producem simbolul monedei euro (numărul de cod 0x20AC):
$UTF8{Euro} = pack( 'U', 0x20AC );
# Echivalent cu: $UTF8{Euro} = "\x{20ac}";
Inspectarea $UTF8{Euro} arată că acesta conține 3 octeți: „\xe2\x82\xac”. Cu toate acestea, acesta
conține doar 1 caracter, numărul 0x20AC. Călătoria dus-întors poate fi completată cu „despachet”:
$Unicode{Euro} = unpack( 'U', $UTF8{Euro} );
Dezambalarea folosind codul șablonului „U” funcționează și pe șirurile de octeți codificate UTF-8.
De obicei, veți dori să împachetați sau să despachetați șiruri UTF-8:
# împachetați și despachetați alfabetul ebraic
my $alefbet = pack( 'U*', 0x05d0..0x05ea );
my @hebrew = unpack( 'U*', $utf );
Vă rugăm să rețineți: în cazul general, este mai bine să utilizați Encode::decode_utf8 pentru a decoda un
Șir de octeți codificat UTF-8 într-un șir Perl Unicode și Encode::encode_utf8 pentru a codifica un
Șir Perl Unicode la UTF-8 octeți. Aceste funcții oferă mijloace de gestionare a octetilor invalidi
secvențe și, în general, au o interfață mai prietenoasă.
O alta Portabil Binar codificare
Codul pachetului „w” a fost adăugat pentru a sprijini o schemă portabilă de codificare a datelor binare care
depășește cu mult numerele întregi simple. (Detalii pot fi găsite lahttp://Casbah.org/>, Scarabeul
proiect.) O bază de stocare de numere întregi nesemnate comprimate BER (Reprezentare codificată binară)
128 de cifre, cifra cea mai semnificativă prima, cu cât mai puține cifre posibil. Bit opt (cel
bit înalt) este setat pe fiecare octet, cu excepția ultimului. Nu există o limită de dimensiune pentru codificarea BER, dar
Perl nu va ajunge la extreme.
my $berbuf = pack( 'w*', 1, 128, 128+1, 128*128+127 );
Un dump hexagonal de $berbuf, cu spații inserate în locurile potrivite, arată 01 8100 8101
81807F. Deoarece ultimul octet este întotdeauna mai mic de 128, „unpack” știe unde să se oprească.
Format Gruparea
Înainte de Perl 5.8, repetările șabloanelor trebuiau făcute prin multiplicarea „x” a
șiruri de șablon. Acum există o modalitate mai bună, deoarece putem folosi codurile pachetului „(” și „)”
combinat cu o numărare repetată. Șablonul „despachetați” din exemplul Stack Frame poate
pur și simplu să fie scris așa:
despachetează( 'v2 (vXXCC)5 v5', $frame )
Să explorăm puțin mai mult această caracteristică. Vom începe cu echivalentul lui
join( '', map( substr( $_, 0, 1 ), @str ) )
care returnează un șir format din primul caracter din fiecare șir. Folosind pack, noi
poate scrie
pack( '(A)'.@str, @str )
sau, pentru că un număr de repetare „*” înseamnă „repetă cât de des este necesar”, pur și simplu
pack( '(A)*', @str )
(Rețineți că șablonul „A*” ar fi împachetat doar $str[0] pe toată lungimea.)
Pentru a împacheta datele stocate ca tripleți (zi, lună, an) într-o matrice @date într-o secvență
de octet, octet, întreg scurt putem scrie
$pd = pack( '(CCS)*', map( @$_, @dates ) );
Pentru a schimba perechi de caractere dintr-un șir (cu lungime egală) se pot folosi mai multe
tehnici. Mai întâi, să folosim „x” și „X” pentru a sări înainte și înapoi:
$s = pack( '(A)*', unpack( '(xAXXAx)*', $s ) );
De asemenea, putem folosi „@” pentru a sări la un offset, 0 fiind poziția în care ne aflam atunci când
ultimul „(” a fost întâlnit:
$s = pack( '(A)*', despachetează( '(@1A @0A @2)*', $s ) );
În cele din urmă, există și o abordare complet diferită prin despachetarea pantalonilor scurți big endian și
împachetarea lor în ordinea inversă a octeților:
$s = pack( '(v)*', unpack( '(n)*', $s );
Lungime și latimi
Şir Lungime
În secțiunea anterioară, am văzut un mesaj de rețea care a fost construit prin prefixul
lungimea mesajului binar la mesajul real. Veți găsi că ambalarea o lungime urmată de
atât de mulți octeți de date este o rețetă folosită frecvent, deoarece adăugarea unui octet nul nu va funcționa
dacă un octet nul poate face parte din date. Iată un exemplu în care sunt utilizate ambele tehnici:
după două șiruri terminate nule cu adresa sursă și destinație, un mesaj scurt (către
un telefon mobil) este trimis după un octet de lungime:
my $msg = pack( 'Z*Z*CA*', $src, $dst, length( $sm ), $sm );
Despachetarea acestui mesaj se poate face cu același șablon:
( $src, $dst, $len, $sm ) = unpack( 'Z*Z*CA*', $msg );
Se așteaptă o capcană subtilă: adăugarea unui alt câmp după mesajul scurt
(în variabila $sm) este în regulă atunci când împachetați, dar acest lucru nu poate fi dezambalat naiv:
# împachetați un mesaj
my $msg = pack( 'Z*Z*CA*C', $src, $dst, length( $sm ), $sm, $prio );
# despachetarea eșuează - $prio rămâne nedefinit!
( $src, $dst, $len, $sm, $prio ) = unpack( 'Z*Z*CA*C', $msg );
Codul pachetului „A*” înghite toți octeții rămași, iar $prio rămâne nedefinit! Înainte de noi
Lasă dezamăgirea să slăbească moralul: Perl are atu pentru a face și acest truc,
doar puțin mai sus pe mânecă. Uita-te la asta:
# împachetați un mesaj: ASCIIZ, ASCIIZ, lungime/șir, octet
my $msg = pack( 'Z* Z* C/A* C', $src, $dst, $sm, $prio );
# despachetează
( $src, $dst, $sm, $prio ) = unpack( 'Z* Z* C/A* C', $msg );
Combinarea a două coduri de pachet cu o bară oblică ("/") le asociază cu o singură valoare din
lista de argumente. În „pachet”, lungimea argumentului este luată și ambalată în funcție de
primul cod în timp ce argumentul în sine este adăugat după ce a fost convertit cu codul șablon
după slash. Acest lucru ne scutește de problemele de a introduce apelul „lungime”, dar este în
„despachetăm” unde punctăm cu adevărat: valoarea octetului de lungime marchează sfârșitul șirului
pentru a fi luate din buffer. Deoarece această combinație nu are sens decât atunci când
codul al doilea pachet nu este „a*”, „A*” sau „Z*”, Perl nu vă va lăsa.
Codul pachetului care precede „/” poate fi orice care este potrivit pentru a reprezenta un număr: Toate
coduri numerice de pachete binare și chiar coduri text, cum ar fi „A4” sau „Z*”:
# împachetați/despachetați un șir precedat de lungimea lui în ASCII
my $buf = pack( 'A4/A*', "Humpty-Dumpty");
# despachetează $buf: „13 Humpty-Dumpty”
my $txt = unpack( 'A4/A*', $buf );
„/” nu este implementat în Perls înainte de 5.6, deci dacă codul dvs. este necesar să funcționeze pe mai vechi
Perls va trebui să „despachetați(’Z* Z* C’)” pentru a obține lungimea, apoi să-l folosiți pentru a crea un nou
despachetează șirul. De exemplu
# împachetați un mesaj: ASCIIZ, ASCIIZ, lungime, șir, octet
# (compatibil 5.005)
my $msg = pack( 'Z* Z* CA* C', $src, $dst, length $sm, $sm, $prio );
# despachetează
( undef, undef, $len) = unpack( 'Z* Z* C', $msg );
($src, $dst, $sm, $prio) = despachetează ( "Z* Z* x A$len C", $msg );
Dar acel al doilea „despachet” se grăbește înainte. Nu folosește un șir literal simplu pentru
șablon. Deci poate ar trebui să prezentăm...
Dinamic Șabloane
Până acum, am văzut literalmente folosite ca șabloane. Dacă lista de articole de pachet nu are
lungime fixă, este necesară o expresie care să construiască șablonul (oricand, pentru unii
motiv, „()*” nu poate fi folosit). Iată un exemplu: Pentru a stoca într-un fel valorile șirurilor numite
care poate fi analizat convenabil de un program C, creăm o secvență de nume și nul
șiruri ASCII terminate, cu „=" între nume și valoare, urmat de un
octet nul suplimentar de delimitare. Iată cum:
my $env = pack( '(A*A*Z*)' . keys( %Env ) . 'C',
map( { ( $_, '=', $Env{$_} ) } taste( %Env ) ), 0 );
Să examinăm roțile acestei morii de octeți, unul câte unul. Există apelul „hartă”, creând
elementele pe care intenționăm să le introducem în buffer-ul $env: la fiecare cheie (în $_) se adaugă „="
separator și valoarea de intrare hash. Fiecare triplet este ambalat cu codul șablonului
secvența „A*A*Z*” care se repetă în funcție de numărul de taste. (Da, asta este
funcția „keys” revine în context scalar.) Pentru a obține ultimul octet nul, adăugăm un 0 la
sfârșitul listei de „pachet”, să fie ambalat cu „C”. (Cititorii atenți s-ar putea să fi observat
că am fi putut omite 0.)
Pentru operația inversă, va trebui să stabilim numărul de articole din buffer
înainte de a putea lăsa „despachetul” să-l strice:
my $n = $env =~ tr/\0// - 1;
my %env = map( split( /=/, $_ ), unpack( "(Z*)$n", $env ) );
„tr” numără octeții nuli. Apelul „depachetează” returnează fiecare o listă de perechi nume-valoare
din care este demontat în blocul „hartă”.
Socoteală Repetari
În loc să stocăm o santinelă la sfârșitul unui articol de date (sau a unei liste de articole), am putea
precedați datele cu un numărător. Din nou, împachetăm cheile și valorile unui hash, precedând fiecare
cu un număr de lungimi scurte nesemnate, iar în față stocăm numărul de perechi:
my $env = pack( 'S(S/A* S/A*)*', chei scalare( %Env ), %Env );
Acest lucru simplifică operația inversă, deoarece numărul de repetări poate fi despachetat
Codul:
my %env = unpack( 'S/(S/A* S/A*)', $env );
Rețineți că acesta este unul dintre rarele cazuri în care nu puteți utiliza același șablon pentru „pachet”
și „despachetați” deoarece „pachetați” nu poate determina un număr de repetare pentru un grup „()”.
Intel HEX
Intel HEX este un format de fișier pentru reprezentarea datelor binare, mai ales pentru programarea diverselor
cipuri, ca fișier text. (Vedeahttp://en.wikipedia.org/wiki/.hex> pentru detalii
descriere șihttp://en.wikipedia.org/wiki/SREC_(format_fișier)> pentru Motorola
Format S-record, care poate fi dezlegat folosind aceeași tehnică.) Fiecare linie începe cu
două puncte (':') și este urmată de o secvență de caractere hexazecimale, specificând un octet
conta n (8 biți), o adresă (16 biți, big endian), un tip de înregistrare (8 biți), n octeți de date și
o sumă de control (8 biți) calculată ca octetul cel mai puțin semnificativ al sumei complementului a doi
octeții precedenți. Exemplu: „:0300300002337A1E”.
Primul pas al procesării unei astfel de linii este conversia, în binar, a hexazecimalului
date, pentru a obține cele patru câmpuri, în timp ce verificați suma de control. Nicio surpriză aici: vom face
începeți cu un apel simplu „pachet” pentru a converti totul în binar:
my $binrec = pack( 'H*', substr( $hexrec, 1 ) );
Secvența de octeți rezultată este cea mai convenabilă pentru verificarea sumei de control. Nu încetini
programați în jos cu o buclă for adăugând valorile „ord” ale octeților acestui șir - „unpack”
codul „%” este lucrul de utilizat pentru a calcula suma de 8 biți a tuturor octeților, care trebuie să fie egală
la zero:
mor dacă nu unpack( "%8C*", $binrec ) == 0;
În sfârșit, să luăm acele patru câmpuri. Până acum, nu ar trebui să aveți probleme cu
primele trei câmpuri - dar cum putem folosi numărul de octeți a datelor din primul câmp ca a
lungime pentru câmpul de date? Aici codurile „x” și „X” vin în ajutor, așa cum permit
sărind înainte şi înapoi în sfoară pentru a despacheta.
my( $adresa, $tip, $date ) = unpack( "xn C X4 C x3 /a", $bin );
Codul „x” omite un octet, deoarece încă nu avem nevoie de numărare. Codul „n” are grijă de
Adresă întreagă big-endian pe 16 biți, iar „C” despachetează tipul de înregistrare. Fiind la offset 4,
unde încep datele, avem nevoie de numărătoare. „X4” ne readuce la punctul unu, care este
octet la offset 0. Acum ridicăm numărul și mărim înainte până la offset 4, unde ne aflăm acum
complet echipat pentru a extrage numărul exact de octeți de date, lăsând suma de control finală
octet singur.
Ambalare și Despachetarea C Structuri
În secțiunile anterioare am văzut cum să împachetăm numere și șiruri de caractere. Dacă ar fi
nu pentru câteva neplăceri am putea încheia această secțiune imediat cu observația concisă
că structurile C nu conțin nimic altceva și, prin urmare, știți deja tot ce există
la ea. Scuze, nu: citiți mai departe, vă rog.
Dacă aveți de-a face cu o mulțime de structuri C și nu doriți să vă spargeți toate șabloanele
șiruri manual, probabil că veți dori să aruncați o privire la modulul CPAN
„Convertire::Binary::C”. Nu numai că poate analiza direct sursa dvs. C, dar are și
în sprijinul tuturor cotelor și scopurilor descrise mai departe în această secțiune.
Aliniere Groapă
Luând în considerare viteza și cerințele de memorie, balanța a fost înclinată
în favoarea executării mai rapide. Acest lucru a influențat modul în care compilatoarele C alocă memorie pentru
structuri: pe arhitecturi în care un operand pe 16 sau 32 de biți poate fi mutat mai rapid între
plasează în memorie, sau către sau de la un registru CPU, dacă este aliniat la un nivel par sau multiplu.
din patru sau chiar la o adresă multiplu de opt, un compilator C vă va oferi această viteză
beneficiază prin introducerea de octeți suplimentari în structuri. Dacă nu traversezi țărmul C asta
nu este de natură să vă provoace vreo durere (deși ar trebui să vă pese când proiectați date mari
structuri, sau doriți ca codul dvs. să fie portabil între arhitecturi (doriți asta,
nu-i asa?)).
Pentru a vedea cum afectează acest lucru „împachetarea” și „despachetul”, vom compara aceste două structuri C:
typedef struct {
char c1;
pantaloni scurti;
char c2;
l lung;
} gappy_t;
typedef struct {
l lung;
pantaloni scurti;
char c1;
char c2;
} dens_t;
De obicei, un compilator C alocă 12 octeți unei variabile „gappy_t”, dar necesită doar 8
octeți pentru un „dense_t”. După ce investigăm acest lucru în continuare, putem desena hărți de memorie, arătând
unde sunt ascunși cei 4 octeți suplimentari:
0 +4 +8 +12
+--+--+--+--+--+--+--+--+--+--+--+--+
|c1|xx| s |c2|xx|xx|xx| l | xx = octet de umplere
+--+--+--+--+--+--+--+--+--+--+--+--+
gappy_t
0 +4 +8
+--+--+--+--+--+--+--+--+
| l | h |c1|c2|
+--+--+--+--+--+--+--+--+
dens_t
Și aici lovește prima ciudatenie: șabloanele „ambalați” și „despachetați” trebuie umplute
cu coduri „x” pentru a obține acei octeți de umplere suplimentari.
Întrebarea firească: „De ce Perl nu poate compensa golurile?” justifică un răspuns. unu
Un motiv bun este că compilatoarele C ar putea oferi extensii (non-ANSI) care să permită tot felul
de control fantezist asupra modului în care structurile sunt aliniate, chiar și la nivelul unui individ
câmpul structurii. Și, dacă acest lucru nu ar fi de ajuns, există un lucru insidios numit „unire”
unde cantitatea de octeți de umplere nu poate fi derivată din alinierea articolului următor
singur.
OK, așa că hai să mușcăm glonțul. Iată o modalitate de a obține alinierea corectă prin inserare
codurile șablonului „x”, care nu iau un articol corespunzător din listă:
my $gappy = pack( 'cxs cxxx l!', $c1, $s, $c2, $l );
Rețineți că „!” după „l”: Vrem să ne asigurăm că împachetăm un întreg lung pe măsură ce este compilat
de compilatorul nostru C. Și chiar și acum, va funcționa doar pentru platformele pe care compilatorul
aliniază lucrurile ca mai sus. Și cineva undeva are o platformă unde nu.
[Probabil un Cray, unde „scurt”, „int” și „lung” sunt toți 8 octeți. :-)]
Numărarea octeților și vizionarea aliniamentelor în structuri lungi este neapărat să fie o greutate. nu este
Există vreo modalitate de a crea șablonul cu un program simplu? Iată un program C care o face
Trucul:
#include
#include
typedef struct {
char fc1;
fs scurt;
char fc2;
fl lung;
} gappy_t;
#define Pt(struct,field,tchar) \
printf( "@%d%s ", offsetof(struct,field), # tchar );
int main () {
Pt( gappy_t, fc1, c);
Pt( gappy_t, fs, s! );
Pt( gappy_t, fc2, c);
Pt( gappy_t, fl, l! );
printf("\n");
}
Linia de ieșire poate fi folosită ca șablon într-un apel „pachetă” sau „despachetează”:
my $gappy = pack( '@0c @2s! @4c @8l!', $c1, $s, $c2, $l );
Gee, încă un cod șablon - de parcă nu am avea destule. Dar „@” ne salvează ziua activând
noi pentru a specifica decalajul de la începutul buffer-ului de pachet la următorul articol: Acesta este
doar valoarea macrocomenzii „offsetof” (definită în „ ") revine atunci când i se oferă a
tipul „struct” și unul dintre numele câmpurilor sale („desemnator de membru” în C standard).
Nici utilizarea decalajelor și nici adăugarea de „x” pentru a acoperi golurile nu este satisfăcătoare. (Doar imagina
ce se întâmplă dacă structura se schimbă.) Ceea ce ne trebuie cu adevărat este un mod de a spune „săriți ca
mulți octeți după cum este necesar la următorul multiplu de N". În Templatese fluent, spui asta
cu „x!N” unde N este înlocuit cu valoarea corespunzătoare. Iată următoarea versiune a noastră
ambalaj struct:
my $gappy = pack( 'cx!2 scx!4 l!', $c1, $s, $c2, $l );
Cu siguranță este mai bine, dar încă trebuie să știm cât de lungi sunt toate numerele întregi și
portabilitatea este departe. Mai degrabă decât 2, de exemplu, vrem să spunem „oricât de lung este scurt
este". Dar acest lucru se poate face prin includerea codului pachetului corespunzător între paranteze: „[s]". Deci,
iată tot ce putem face mai bine:
my $gappy = pack( 'cx![s] scx![l!] l!', $c1, $s, $c2, $l );
Tratarea implementate cu Endian-ness
Acum, imaginați-vă că vrem să împachetăm datele pentru o mașină cu o altă ordine de octeți.
În primul rând, va trebui să ne dăm seama cât de mari sunt cu adevărat tipurile de date de pe mașina țintă.
Să presupunem că lățimea lungi este de 32 de biți și cea scurtă de 16 biți. Poți atunci
rescrie șablonul ca:
my $gappy = pack( 'cx![s] scx![l] l', $c1, $s, $c2, $l );
Dacă mașina țintă este little-endian, am putea scrie:
my $gappy = pack( 'cx![s] s< cx![l] l<', $c1, $s, $c2, $l );
Acest lucru îi forțează pe membrii scurti și pe cei lungi să fie little-endian și este bine dacă tu
nu au prea mulți membri struct. Dar am putea folosi și modificatorul de ordine de octeți pe a
grupați și scrieți următoarele:
my $gappy = pack( '( cx![s] scx![l] l )<', $c1, $s, $c2, $l );
Acest lucru nu este la fel de scurt ca înainte, dar face mai evident faptul că intenționăm să avem
Little-endian byte-order pentru un întreg grup, nu numai pentru coduri de șablon individuale. Se poate
de asemenea, să fie mai lizibile și mai ușor de întreținut.
Aliniere, Lua 2
Mi-e teamă că nu am terminat încă cu captura de aliniere. Hidra se ridică
un alt cap urât când împachetezi matrice de structuri:
typedef struct {
numărare scurtă;
glif char;
} cell_t;
typedef cell_t buffer_t[BUFLEN];
Unde-i captura? Completarea nu este necesară nici înaintea primului câmp „număr”, nici între
acesta și următorul câmp „glif”, așa că de ce nu putem împacheta pur și simplu așa:
# ceva nu merge bine aici:
pack( 's!a' x @buffer,
harta{ ( $_->{count}, $_->{glif} ) } @buffer );
Acesta include „3*@buffer” octeți, dar se dovedește că dimensiunea „buffer_t” este de patru ori
„BUFLEN”! Morala poveștii este că alinierea necesară a unei structuri sau matrice este
propagat la nivelul următor superior, unde trebuie să luăm în considerare umplutura at il capăt fiecărei
de asemenea componenta. Astfel, șablonul corect este:
pack( 's!ax' x @buffer,
harta{ ( $_->{count}, $_->{glif} ) } @buffer );
Aliniere, Lua 3
Și chiar dacă țineți cont de toate cele de mai sus, ANSI încă permite acest lucru:
typedef struct {
char foo[2];
} picior;
variază în mărime. Constrângerea de aliniere a structurii poate fi mai mare decât oricare dintre ele
elemente. [Și dacă credeți că acest lucru nu afectează nimic comun, dezmembrați următorul
telefonul mobil pe care îl vedeți. Mulți au nuclee ARM, iar regulile de structură ARM fac „sizeof
(foo_t)" == 4]
Pointeri pentru Cum la Utilizare Lor
Titlul acestei secțiuni indică a doua problemă pe care o puteți întâlni mai devreme sau mai târziu
când împachetați structuri C. Dacă funcția pe care intenționați să o apelați așteaptă un, să spunem „void *”
valoare, tu nu poti pur și simplu luați o referință la o variabilă Perl. (Deși această valoare
cu siguranță este o adresă de memorie, nu este adresa unde se află conținutul variabilei
stocate.)
Codul șablonului „P” promite să împacheteze un „indicator către un șir de lungime fixă”. Nu asta este
noi vrem? Sa incercam:
# alocați spațiu de stocare și împachetați un pointer către acesta
my $memory = "\x00" x $size;
my $memptr = pack( 'P', $memorie );
Dar așteaptă: „împachetarea” nu returnează doar o secvență de octeți? Cum putem trece acest șir de
octeți către un cod C care se așteaptă la un pointer care, la urma urmei, nu este altceva decât un număr? The
răspunsul este simplu: Trebuie să obținem adresa numerică din octeții returnați de „pack”.
my $ptr = unpack( 'L!', $memptr );
În mod evident, acest lucru presupune că este posibil să tipăriți un pointer către un lung și nesemnat
invers, care funcționează frecvent, dar nu ar trebui luată ca o lege universală. - Acum că
avem acest indicator, următoarea întrebare este: Cum îl putem folosi? Avem nevoie de un apel
la o funcție C unde este așteptat un pointer. The citit(2) îmi vine în minte apelul de sistem:
ssiize_t read(int fd, void *buf, size_t count);
După ce citim perlfunc care explică cum să folosiți „syscall”, putem scrie această funcție Perl
copierea unui fișier în ieșirea standard:
necesită „syscall.ph”; # rulați h2ph pentru a genera acest fișier
sub cat($){
my $path = shift();
my $size = -s $cale;
my $memory = "\x00" x $size; # alocă puțină memorie
my $ptr = unpack( 'L', pack( 'P', $memorie) );
deschide( F, $cale ) || die("$cale: nu se poate deschide ($!)\n");
my $fd = fileno(F);
my $res = syscall( &SYS_read, fileno(F), $ptr, $size );
print $memorie;
aproape (F);
}
Acesta nu este nici un exemplar de simplitate, nici un model de portabilitate, dar ilustrează
Ideea: putem să ne furișăm în culise și să accesăm Perl, altfel bine păzit.
memorie! (Notă importantă: „syscall” de la Perl face nu vă solicită să construiți pointeri în
acest sens giratoriu. Pur și simplu treceți o variabilă șir, iar Perl trimite adresa.)
Cum funcționează „despachetul” cu „P”? Imaginați-vă un indicator în buffer pe cale să fie despachetat:
Dacă nu este indicatorul nul (care va produce inteligent valoarea „undef”) avem un
adresa de început - dar apoi ce? Perl nu are de unde să știe cât durează această „lungime fixă
șir" este, așa că depinde de dvs. să specificați dimensiunea reală ca lungime explicită după "P".
my $mem = "abcdefghijklmn";
print unpack( 'P5', pack( 'P', $mem) ); # afișează „abcde”
În consecință, „pachet” ignoră orice număr sau „*” după „P”.
Acum că am văzut „P” la lucru, am putea la fel de bine să dăm „p” un vârtej. De ce avem nevoie de un
al doilea cod șablon pentru împachetarea indicatoarelor? Răspunsul se află în spatele faptului simplu
că un „despachet” cu „p” promite un șir terminat în nul care începe la adresa luată
din buffer și asta implică o lungime pentru articolul de date care urmează să fie returnat:
my $buf = pack( 'p', "abc\x00efhijklmn" );
print unpack('p', $buf); # afișează „abc”
Deși acest lucru poate fi confuz: ca o consecință a lungimii implicate de
lungimea șirului, un număr după codul pachetului „p” este un număr de repetări, nu o lungime ca după
„P”.
Folosirea „pack(..., $x)” cu „P” sau „p” pentru a obține adresa unde este de fapt stocat $x trebuie
fi folosit cu circumspecție. Mașina internă a lui Perl ia în considerare relația dintre a
variabilă și acea adresă ca fiind o chestiune privată și nu ne interesează cu adevărat că noi
au obținut o copie. Prin urmare:
· Nu utilizați „pachet” cu „p” sau „P” pentru a obține adresa variabilei care urmează să apară
din domeniul de aplicare (și, prin urmare, eliberându-și memoria) înainte de a termina cu utilizarea
memorie la adresa respectivă.
· Fiți foarte atenți la operațiunile Perl care modifică valoarea variabilei. Adăugând
ceva legat de variabilă, de exemplu, ar putea necesita realocarea stocării sale,
lăsându-te cu un indicator către pământul nimănui.
· Nu credeți că puteți obține adresa unei variabile Perl atunci când este stocată ca un
număr întreg sau dublu! "pack('P', $x)" va forța variabila internă
reprezentare în șir, la fel ca și cum ați fi scris ceva de genul „$x .= ''”.
Cu toate acestea, este sigur să împachetați P sau p un șir literal, deoarece Perl pur și simplu alocă un
variabilă anonimă.
Ambalaj Recipes, Romania
Iată o colecție de (eventual) rețete de conserve utile pentru „împachetați” și „despachetați”:
# Convertiți adresa IP pentru funcțiile socket
pack( "C4", split /\./, "123.4.5.6");
# Numărați biții dintr-o bucată de memorie (de exemplu, un vector selectat)
unpack('%32b*', $mask);
# Determinați caracterul final al sistemului dvs
$is_little_endian = unpack( 'c', pack( 's', 1 ) );
$is_big_endian = unpack( 'xc', pack( 's', 1 ) );
# Determinați numărul de biți dintr-un întreg nativ
$biți = unpack( '%32I!', ~0);
# Pregătiți argumentul pentru apelul de sistem nanosleep
my $timespec = pack( 'L!L!', $secs, $nanosecs );
Pentru o simplă descărcare de memorie, despachetăm câțiva octeți în tot atâtea perechi de cifre hexadecimale și
utilizați „hartă” pentru a gestiona spațierea tradițională - 16 octeți la o linie:
al meu $i;
tipăriți harta( ++$i % 16 ? "$_ " : "$_\n",
unpack('H2' x lungime($mem), $mem)),
lungime( $mem ) % 16 ? "\n" : '';
Funnies Secțiune
# Străgând cifre din senin...
print unpack('C', pack('x') ),
unpack('%B*', pack('A') ),
unpack('H', pack('A') ),
unpack('A', unpack('C', pack('A' ) ) ), "\n";
# Una pentru drum ;-)
my $advice = pack( 'tot ce poți într-o dubă');
Autori
Simon Cozens și Wolfgang Laun.
Utilizați perlpacktut online folosind serviciile onworks.net