Il s'agit de la commande perlpacktut qui peut être exécutée dans le fournisseur d'hébergement gratuit OnWorks en utilisant l'un de nos multiples postes de travail en ligne gratuits tels que Ubuntu Online, Fedora Online, l'émulateur en ligne Windows ou l'émulateur en ligne MAC OS
PROGRAMME:
Nom
perlpacktut - tutoriel sur "pack" et "unpack"
DESCRIPTION
« pack » et « unpack » sont deux fonctions permettant de transformer des données selon une méthode définie par l'utilisateur.
modèle, entre la manière protégée dont Perl stocke les valeurs et une représentation bien définie
comme cela peut être requis dans l'environnement d'un programme Perl. Malheureusement, ils sont aussi deux
des fonctions les plus mal comprises et les plus souvent négligées de Perl. Cette
tutoriel les démystifiera pour vous.
Le manuel de formation Basic Principe
La plupart des langages de programmation n'abritent pas la mémoire où sont stockées les variables. En C, pour
exemple, vous pouvez prendre l'adresse d'une variable, et l'opérateur "sizeof" vous dit
combien d'octets sont alloués à la variable. En utilisant l'adresse et la taille, vous pouvez
accéder au stockage au contenu de votre coeur.
En Perl, vous ne pouvez tout simplement pas accéder à la mémoire au hasard, mais la structure et la représentation
la conversion fournie par "pack" et "unpack" est une excellente alternative. Le pack"
la fonction convertit les valeurs en une séquence d'octets contenant des représentations selon un
spécification donnée, l'argument dit "modèle". "déballer" est le processus inverse,
déduire certaines valeurs du contenu d'une chaîne d'octets. (Attention, cependant, que
tout ce qui a été emballé ne peut pas être soigneusement déballé - une expérience très courante car
les voyageurs aguerris confirmeront probablement.)
Pourquoi, pourriez-vous demander, auriez-vous besoin d'un morceau de mémoire contenant des valeurs en binaire
représentation? Une bonne raison est l'entrée et la sortie accédant à un fichier, un périphérique ou un
connexion réseau, par laquelle cette représentation binaire vous est soit imposée, soit
vous donner un certain avantage dans le traitement. Une autre cause est la transmission de données à un appel système
qui n'est pas disponible en tant que fonction Perl : "syscall" vous oblige à fournir des paramètres
stocké de la manière dont cela se produit dans un programme C. Même le traitement de texte (comme indiqué dans le prochain
section) peut être simplifié par une utilisation judicieuse de ces deux fonctions.
Pour voir comment fonctionne le (dé)packing, nous allons commencer par un simple modèle de code où la conversion
est en petite vitesse : entre le contenu d'une séquence d'octets et une chaîne de caractères hexadécimaux
chiffres. Utilisons "unpack", car cela est susceptible de vous rappeler un programme de vidage, ou
dernier message désespéré que les programmes malheureux ont l'habitude de vous lancer avant d'expirer
dans le bleu sauvage là-bas. En supposant que la variable $mem contient une séquence d'octets qui
nous aimerions inspecter sans rien supposer sur sa signification, nous pouvons écrire
my( $hex ) = unpack( 'H*', $mem );
print "$hex\n" ;
sur quoi nous pourrions voir quelque chose comme ceci, avec chaque paire de chiffres hexadécimaux correspondant à
un octet :
41204d414e204120504c414e20412043414e414c2050414e414d41
Qu'y avait-il dans ce morceau de mémoire ? Chiffres, caractères ou un mélange des deux ? En admettant que
nous sommes sur un ordinateur où l'encodage ASCII (ou similaire) est utilisé : valeurs hexadécimales dans
la plage 0x40 - 0x5A indique une lettre majuscule et 0x20 code un espace. Alors on pourrait
supposez qu'il s'agit d'un morceau de texte, que certains sont capables de lire comme un tabloïd ; mais d'autres le feront
devez mettre la main sur une table ASCII et revivre ce sentiment de première année. Ne s'en soucie pas trop
beaucoup sur la façon de lire ceci, nous notons que "décompresser" avec le code de modèle "H"
convertit le contenu d'une séquence d'octets dans la notation hexadécimale habituelle.
Étant donné que « une séquence de » est une indication assez vague de quantité, « H » a été défini pour
convertir juste un seul chiffre hexadécimal à moins qu'il ne soit suivi d'un nombre de répétitions. Un
l'astérisque pour le nombre de répétitions signifie utiliser ce qui reste.
L'opération inverse - emballage du contenu d'octets à partir d'une chaîne de chiffres hexadécimaux - est
tout aussi facilement écrit. Par exemple:
mon $s = pack( 'H2' x 10, 30..39 );
print "$s\n" ;
Puisque nous alimentons une liste de dix chaînes hexadécimales à 2 chiffres pour "pack", le modèle de pack
doit contenir dix codes de pack. Si cela est exécuté sur un ordinateur avec un codage de caractères ASCII,
il imprimera 0123456789.
Emballage Texte
Supposons que vous deviez lire dans un fichier de données comme celui-ci :
Date |Description | Revenu|Dépense
01/24/2001 Zed's Camel Emporium 1147.99
01/28/2001 Spray anti-puces 24.99
01/29/2001 Promenades à dos de chameau aux touristes 235.00
Comment faisons-nous ça? Vous pourriez penser d'abord à utiliser « split » ; cependant, puisque "split" s'effondre
champs vides, vous ne saurez jamais si un enregistrement était un revenu ou une dépense. Oups. Bien,
vous pouvez toujours utiliser "substr":
tandis que (<>) {
ma $date = substr($_, 0, 11);
mon $desc = substr($_, 12, 27);
mon $revenu = substr($_, 40, 7);
mon $dépense = substr($_, 52, 7);
...
}
Ce n'est pas vraiment un baril de rires, n'est-ce pas ? En fait, c'est pire qu'il n'y paraît ; les
les yeux d'aigle peuvent remarquer que le premier champ ne doit avoir que 10 caractères de large, et le
l'erreur s'est propagée à travers les autres nombres - que nous avons dû compter à la main.
C'est donc sujet aux erreurs et horriblement hostile.
Ou peut-être pourrions-nous utiliser des expressions régulières :
tandis que (<>) {
my($date, $desc, $revenu, $dépense) =
m|(\d\d/\d\d/\d{4}) (.{27}) (.{7})(.*)| ;
...
}
Urgh. Bon, c'est un peu mieux, mais - bon, voudriez-vous maintenir ça ?
Hé, Perl n'est-il pas censé rendre ce genre de chose facile ? Eh bien, c'est le cas, si vous utilisez le
les bons outils. "pack" et "unpack" sont conçus pour vous aider lorsque vous faites face à des problèmes de
des données de largeur comme ci-dessus. Regardons une solution avec "unpack":
tandis que (<>) {
my($date, $desc, $revenu, $dépense) = unpack("A10xA27xA7A*", $_);
...
}
Cela a l'air un peu plus joli; mais nous devons démonter ce modèle étrange. Où ai-je tiré
que hors?
OK, jetons à nouveau un coup d'œil à certaines de nos données ; en fait, nous inclurons les en-têtes et un
règle pratique pour que nous puissions savoir où nous sommes.
1 2 3 4 5
1234567890123456789012345678901234567890123456789012345678
Date |Description | Revenu|Dépense
01/28/2001 Spray anti-puces 24.99
01/29/2001 Promenades à dos de chameau aux touristes 235.00
De là, nous pouvons voir que la colonne de date s'étend de la colonne 1 à la colonne 10 - dix
caractères larges. Le "pack"-ese pour "caractère" est "A", et dix d'entre eux sont "A10". Donc si
on voulait juste extraire les dates, on pourrait dire ceci :
mon($date) = unpack("A10", $_);
D'accord, quelle est la prochaine étape ? Entre la date et la description se trouve une colonne vide ; nous voulons sauter
dessus. Le modèle "x" signifie "sauter en avant", nous en voulons donc un. Ensuite, nous avons
un autre lot de caractères, de 12 à 38. Cela fait 27 caractères de plus, d'où "A27". (Ne pas
faites l'erreur du poteau de clôture - il y a 27 caractères entre 12 et 38, pas 26. Comptez-les !)
Maintenant, nous sautons un autre caractère et prenons les 7 caractères suivants :
my($date,$description,$revenu) = unpack("A10xA27xA7", $_);
Vient maintenant le peu intelligent. Les lignes de notre grand livre qui ne sont que des revenus et non des dépenses
peut se terminer à la colonne 46. Par conséquent, nous ne voulons pas dire à notre modèle de "déballage" que nous need à
trouver 12 autres caractères; nous dirons simplement "s'il reste quelque chose, prenez-le". Comme toi
pourrait deviner à partir d'expressions régulières, c'est ce que le "*" signifie : "utiliser tout
restant".
· Soyez averti, cependant, que contrairement aux expressions régulières, si le modèle "décompresser" ne
correspondent aux données entrantes, Perl criera et mourra.
Par conséquent, en rassemblant le tout :
mon ($date, $description, $revenu, $dépense) =
unpack("A10xA27xA7xA*", $_);
Maintenant, ce sont nos données analysées. Je suppose que ce que nous pourrions vouloir faire maintenant est de totaliser nos revenus
et les dépenses, et ajouter une autre ligne à la fin de notre grand livre - dans le même format -
dire combien nous avons rapporté et combien nous avons dépensé :
tandis que (<>) {
mon (date, $desc, $revenu, $dépense) =
unpack("A10xA27xA7xA*", $_);
$tot_revenu += $revenu ;
$tot_expend += $expend ;
}
$tot_ Income = sprintf("%.2f", $tot_ Income); # Faites-leur entrer
$tot_expend = sprintf("%.2f", $tot_expend); # format "financier"
$date = POSIX::strftime("%m/%d/%Y", heure locale);
# OK allons-y:
pack d'impression("A10xA27xA7xA*", $date, "Totaux",
$tot_revenu, $tot_expend);
Oh, hum. Cela n'a pas tout à fait fonctionné. Voyons ce qui se passe:
01/24/2001 Zed's Camel Emporium 1147.99
01/28/2001 Spray anti-puces 24.99
01/29/2001 Promenades à dos de chameau aux touristes 1235.00
03/23/2001Totaux 1235.001172.98
OK, c'est un début, mais qu'est-il arrivé aux espaces ? On a mis "x", n'est-ce pas ? Ne devrait-il pas
aller de l'avant ? Regardons ce que "pack" dans perlfunc dit :
x Un octet nul.
Urgh. Pas étonnant. Il y a une grande différence entre "un octet nul", le caractère zéro et "un
espace", caractère 32. Perl a mis quelque chose entre la date et la description - mais
malheureusement, on ne le voit pas !
Ce que nous devons en fait faire, c'est étendre la largeur des champs. Le format « A » comble tout
caractères inexistants avec des espaces, nous pouvons donc utiliser les espaces supplémentaires pour aligner nos
champs, comme ceci :
pack d'impression ("A11 A28 A8 A*", $date, "Totaux",
$tot_revenu, $tot_expend);
(Notez que vous pouvez mettre des espaces dans le modèle pour le rendre plus lisible, mais ils ne le font pas
traduire en espaces dans la sortie.) Voici ce que nous avons cette fois :
01/24/2001 Zed's Camel Emporium 1147.99
01/28/2001 Spray anti-puces 24.99
01/29/2001 Promenades à dos de chameau aux touristes 1235.00
03/23/2001 Totaux 1235.00 1172.98
C'est un peu mieux, mais nous avons toujours cette dernière colonne qui doit être déplacée plus loin
plus de. Il existe un moyen simple de résoudre ce problème : malheureusement, nous ne pouvons pas obtenir "pack" à droite-
justifier nos champs, mais nous pouvons demander à "sprintf" de le faire :
$tot_ Income = sprintf("%.2f", $tot_ Income);
$tot_expend = sprintf("%12.2f", $tot_expend);
$date = POSIX::strftime("%m/%d/%Y", heure locale);
pack d'impression ("A11 A28 A8 A*", $date, "Totaux",
$tot_revenu, $tot_expend);
Cette fois, nous obtenons la bonne réponse :
01/28/2001 Spray anti-puces 24.99
01/29/2001 Promenades à dos de chameau aux touristes 1235.00
03/23/2001 Totaux 1235.00 1172.98
C'est ainsi que nous consommons et produisons des données à largeur fixe. Récapitulons ce que nous avons vu de
"pack" et "unpack" jusqu'à présent :
· Utilisez « pack » pour passer de plusieurs données à une version à largeur fixe ; utiliser "décompresser"
pour transformer une chaîne au format à largeur fixe en plusieurs éléments de données.
· Le format de pack « A » signifie « tout caractère » ; si vous "faites vos bagages" et que vous n'avez plus de
choses à emballer, "pack" remplira le reste d'espaces.
· "x" signifie "sauter un octet" lors du "déballage" ; quand "pack"ing, cela signifie "introduire un null
octet" - ce n'est probablement pas ce que vous voulez dire si vous avez affaire à du texte brut.
· Vous pouvez suivre les formats avec des nombres pour dire combien de caractères doivent être affectés
par ce format : « A12 » signifie « prendre 12 caractères » ; "x6" signifie "sauter 6 octets" ou
"caractère 0, 6 fois".
· Au lieu d'un nombre, vous pouvez utiliser "*" pour signifier "consommer tout le reste".
Avertissement: lors de l'emballage de plusieurs éléments de données, "*" signifie uniquement "consommer tous les
donnée actuelle". C'est-à-dire
pack("A*A*", $un, $deux)
emballe tous les $ un dans le premier « A* », puis tous les $ deux dans le second. C'est un
principe général : chaque caractère de format correspond à une donnée à
"emballé.
Emballage Nombres
Tant pis pour les données textuelles. Passons aux trucs charnus que « emballer » et « déballer » sont les meilleurs
at : gestion des formats binaires pour les nombres. Il n'y a bien sûr pas qu'un seul format binaire
- la vie serait trop simple - mais Perl fera tout le travail délicat pour vous.
Entiers
Les numéros d'emballage et de déballage impliquent la conversion vers et depuis certains groupe de neurones binaire
représentation. Laissant pour le moment les nombres à virgule flottante de côté, le saillant
les propriétés d'une telle représentation sont :
· le nombre d'octets utilisés pour stocker l'entier,
· si le contenu est interprété comme un numéro signé ou non signé,
· l'ordre des octets : si le premier octet est l'octet de poids faible ou de poids fort (ou :
little-endian ou big-endian, respectivement).
Ainsi, par exemple, pour emballer 20302 à un entier signé de 16 bits dans votre ordinateur
représentation que vous écrivez
mon $ps = pack( 's', 20302 );
Encore une fois, le résultat est une chaîne, contenant maintenant 2 octets. Si vous imprimez cette chaîne (c'est-à-dire,
généralement, non recommandé) vous pourriez voir "ON" ou "NO" (selon l'octet de votre système
commande) - ou quelque chose de complètement différent si votre ordinateur n'utilise pas de caractère ASCII
codage. Décompresser $ps avec le même modèle renvoie la valeur entière d'origine :
my( $s ) = unpack( 's', $ps );
Ceci est vrai pour tous les codes de modèles numériques. Mais ne vous attendez pas à des miracles : si les
la valeur dépasse la capacité d'octets allouée, les bits de poids fort sont ignorés en silence et
déballer ne pourra certainement pas les sortir d'un chapeau magique. Et, quand vous emballez
en utilisant un code de modèle signé tel que "s", une valeur excessive peut entraîner le bit de signe
être défini, et le décompresser renverra intelligemment une valeur négative.
16 bits ne vous mèneront pas trop loin avec des entiers, mais il y a "l" et "L" pour signé et
entiers 32 bits non signés. Et si cela ne suffit pas et que votre système prend en charge le 64 bits
entiers, vous pouvez repousser les limites beaucoup plus près de l'infini avec les codes de pack "q" et "Q". UNE
une exception notable est fournie par les codes de pack "i" et "I" pour les entiers signés et non signés
de la variété "local custom": Un tel entier prendra autant d'octets qu'un C local
le compilateur renvoie "sizeof(int)", mais il utilisera at au 32 XNUMX bits.
Chacun des codes de pack entiers "sSlLqQ" se traduit par un nombre fixe d'octets, peu importe
où vous exécutez votre programme. Cela peut être utile pour certaines applications, mais cela ne
fournir un moyen portable pour passer des structures de données entre les programmes Perl et C (lié à
se produire lorsque vous appelez des extensions XS ou la fonction Perl "syscall"), ou lorsque vous lisez ou
écrire des fichiers binaires. Ce dont vous aurez besoin dans ce cas, ce sont des codes de modèle qui dépendent de ce
votre compilateur C local compile lorsque vous codez "short" ou "unsigned long", par exemple.
Ces codes et leurs longueurs d'octets correspondantes sont indiqués dans le tableau ci-dessous. Depuis le
Le standard C laisse beaucoup de latitude quant aux tailles relatives de ces types de données,
les valeurs réelles peuvent varier, et c'est pourquoi les valeurs sont données sous forme d'expressions en C et Perl.
(Si vous souhaitez utiliser les valeurs de %Config dans votre programme, vous devez l'importer avec "use
Configuration".)
signé non signé longueur d'octet en C longueur d'octet en Perl
s! S ! sizeof(short) $Config{shortsize}
je! JE! sizeof(int) $Config{intsize}
moi ! L ! sizeof(long) $Config{longsize}
q! Q ! sizeof(long long) $Config{longlongsize}
Le "je!" et moi!" les codes ne sont pas différents de "i" et "I" ; ils sont tolérés pour
l'amour de l'exhaustivité.
Déballage a Stack Cadre métallique robuste
La demande d'un ordre d'octets particulier peut être nécessaire lorsque vous travaillez avec des données binaires
provenant d'une architecture spécifique alors que votre programme pourrait fonctionner sur un tout
système différent. Par exemple, supposons que vous ayez 24 octets contenant un cadre de pile car il
se passe sur un Intel 8086 :
+---------+ +----+----+ +--------------+
CGU : | IP | TOS+4 :| FL | FH | DRAPEAUX TOS+14 :| SI |
+---------+ +----+----+ +--------------+
| CS | | AL | AH | HACHE | DI |
+---------+ +----+----+ +--------------+
| BL | BH | BX | PB |
+----+----+ +---------+
| CL | CH | CX | DS |
+----+----+ +---------+
| DL | DH | DX | ES |
+----+----+ +---------+
Tout d'abord, nous notons que ce processeur 16 bits séculaire utilise l'ordre little-endian, et c'est pourquoi
l'octet de poids faible est stocké à l'adresse inférieure. Pour déballer un tel court (non signé), nous allons
devez utiliser le code "v". Un compte répété déballe les 12 shorts :
mon( $ip, $cs, $flags, $ax, $bx, $cd, $dx, $si, $di, $bp, $ds, $es ) =
unpack( 'v12', $frame );
Alternativement, nous aurions pu utiliser "C" pour décompresser les registres d'octets accessibles individuellement
FL, FH, AL, AH… :
ma( $fl, $fh, $al, $ah, $bl, $bh, $cl, $ch, $dl, $dh ) =
unpack( 'C10', substr( $frame, 4, 10 ) );
Ce serait bien si on pouvait faire ça d'un seul coup : déballer un short, reculer un peu,
puis décompressez 2 octets. Depuis Perl is sympa, il propose le code du modèle "X" à sauvegarder
un octet. En mettant tout cela ensemble, nous pouvons maintenant écrire :
mon( $ip, $cs,
$drapeaux,$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 construction maladroite du modèle peut être évitée - il suffit de lire la suite !)
Nous avons pris soin de construire le modèle afin qu'il corresponde au contenu de notre
tampon de trame. Sinon, nous obtiendrions des valeurs non définies ou "unpack" ne pourrait pas décompresser
tous. Si "pack" n'a plus d'éléments, il fournira des chaînes nulles (qui sont forcées dans
zéros chaque fois que le code du pack l'indique).
Comment la à Manger an Œuf on a Net
Le code pack pour big-endian (octet de poids fort à l'adresse la plus basse) est "n" pour 16 bits et
"N" pour les entiers de 32 bits. Vous utilisez ces codes si vous savez que vos données proviennent d'un
architecture conforme, mais, assez étonnamment, vous devriez également utiliser ces codes de pack si
vous échangez des données binaires, à travers le réseau, avec un système que vous connaissez à côté
rien à propos. La simple raison est que cet ordre a été choisi comme réseau de commander,
et tous les programmes craignant la norme devraient suivre cette convention. (Il s'agit bien entendu d'un
soutien sévère à l'un des partis lilliputiens et pourrait bien influencer les
développement là-bas.) Donc, si le protocole s'attend à ce que vous envoyiez un message en envoyant le
longueur d'abord, suivi d'autant d'octets, vous pouvez écrire :
my $buf = pack( 'N', length( $msg ) ) . $msg ;
ou même:
my $buf = pack( 'NA*', length( $msg ), $msg );
et transmettez $buf à votre routine d'envoi. Certains protocoles exigent que le décompte inclue
la longueur du décompte lui-même : puis ajoutez simplement 4 à la longueur des données. (Mais assurez-vous de lire
"Longueurs et largeurs" avant de vraiment coder cela !)
Ordre des octets modificateurs
Dans les sections précédentes, nous avons appris à utiliser "n", "N", "v" et "V" pour emballer et décompresser
entiers avec un ordre d'octets grand ou petit-boutiste. Même si c'est sympa, c'est quand même plutôt
limité car il laisse de côté toutes sortes d'entiers signés ainsi que les entiers 64 bits. Pour
exemple, si vous vouliez décompresser une séquence d'entiers 16 bits big-endian signés dans un
de manière indépendante de la plate-forme, vous devrez écrire :
my @data = unpack 's*', pack 'S*', unpack 'n*', $buf;
C'est moche. Depuis Perl 5.9.2, il existe une manière bien plus agréable d'exprimer votre désir d'un
certain ordre des octets : les modificateurs ">" et "<". ">" est le modificateur big-endian, tandis que "<"
est le modificateur little-endian. En les utilisant, nous pourrions réécrire le code ci-dessus comme suit :
my @data = unpack 's>*', $buf;
Comme vous pouvez le voir, le "gros bout" de la flèche touche le "s", ce qui est une belle façon de
rappelez-vous que ">" est le modificateur big-endian. Il en va évidemment de même pour "<", où le
"petit bout" touche le code.
Vous trouverez probablement ces modificateurs encore plus utiles si vous devez faire face à de gros ou
structures C little-endian. Assurez-vous de lire "Emballage et déballage des structures C" pour en savoir plus
sur ça.
Flottant point Nombres
Pour emballer des nombres à virgule flottante, vous avez le choix entre les codes d'emballage "f", "d",
"F" et "D". "f" et "d" emballent dans (ou déballent de) simple précision ou double précision
représentation telle qu'elle est fournie par votre système. Si votre système le prend en charge, "D" peut être
utilisé pour compresser et décompresser les valeurs ("long double"), ce qui peut offrir encore plus de résolution que
"f" ou "d". Note qui là sommes-nous différent Long double formats.
"F" contient un "NV", qui est le type à virgule flottante utilisé par Perl en interne.
Il n'existe pas de représentation de réseau pour de vrais, donc si vous voulez envoyer votre
nombres réels au-delà des limites de l'ordinateur, vous feriez mieux de vous en tenir à la représentation textuelle,
éventuellement en utilisant le format flottant hexadécimal (en évitant la perte de conversion décimale), sauf si
vous êtes absolument sûr de ce qu'il y a à l'autre bout du fil. Pour encore plus
aventureux, vous pouvez également utiliser les modificateurs d'ordre d'octet de la section précédente sur
codes à virgule flottante.
Exotique Gabarits
Bit String
Les bits sont les atomes dans le monde de la mémoire. L'accès aux bits individuels peut devoir être utilisé
soit en dernier recours, soit parce que c'est le moyen le plus pratique de gérer vos données. Bit
string (un)packing convertit entre les chaînes contenant une série de 0 et 1 caractères et
une séquence d'octets contenant chacun un groupe de 8 bits. C'est presque aussi simple que ça
sons, sauf qu'il y a deux façons dont le contenu d'un octet peut être écrit comme un bit
chaîne de caractères. Regardons un octet annoté :
7 6 5 4 3 2 1 0
+ ----------------- +
| 1 0 0 0 1 1 0 0 |
+ ----------------- +
ESM DSL
C'est encore une fois de manger des œufs : certains pensent que cela devrait être écrit comme une chaîne de bits
"10001100" c'est-à-dire en commençant par le bit de poids fort, d'autres insistent sur "00110001".
Eh bien, Perl n'est pas biaisé, c'est pourquoi nous avons des codes de chaîne à deux bits :
$byte = pack( 'B8', '10001100' ); # commencer par MSB
$byte = pack( 'b8', '00110001' ); # commencer par LSB
Il n'est pas possible de compresser ou de décompresser les champs de bits - juste des octets entiers. "pack" toujours
commence à la limite d'octet suivante et "arrondit" au prochain multiple de 8 en ajoutant zéro
bits au besoin. (Si vous voulez des champs de bits, il y a "vec" dans perlfunc. Ou vous pouvez
implémenter la gestion des champs de bits au niveau de la chaîne de caractères, en utilisant split, substr et
concaténation sur des chaînes de bits non compressées.)
Pour illustrer le déballage des chaînes de bits, nous allons décomposer un simple registre d'état (un "-"
signifie un bit "réservé") :
+-----------------+-----------------+
| SZ-A-P-C | - - - - ODIT |
+-----------------+-----------------+
MSB LSB MSB LSB
La conversion de ces deux octets en une chaîne peut être effectuée avec le modèle de décompression 'b16'. À
obtenir les valeurs de bits individuelles à partir de la chaîne de bits que nous utilisons "split" avec le "vide"
modèle de séparateur qui dissèque en caractères individuels. Valeurs en bits du
les positions "réservées" sont simplement attribuées à "undef", une notation pratique pour "Je ne
attention où cela va".
($carry, undef, $parity, undef, $auxcarry, undef, $zero, $sign,
$trace, $interruption, $direction, $débordement) =
split( //, unpack( 'b16', $status ) );
Nous aurions tout aussi bien pu utiliser un modèle de décompression 'b12', puisque les 4 derniers bits peuvent être
ignoré de toute façon.
Uuencodage
Un autre impair dans l'alphabet modèle est "u", qui contient une "chaîne uuencodée".
("uu" est l'abréviation de Unix-to-Unix.) Il y a de fortes chances que vous n'ayez jamais besoin de cet encodage
technique qui a été inventée pour surmonter les lacunes de la transmission à l'ancienne
supports qui ne prennent en charge que de simples données ASCII. La recette indispensable est simple :
Prenez trois octets, ou 24 bits. Divisez-les en 4 packs de six, en ajoutant un espace (0x20) à chacun.
Répétez jusqu'à ce que toutes les données soient mélangées. Plier des groupes de 4 octets en lignes ne dépassant pas
60 et garnissez-les devant le nombre d'octets d'origine (incrémenté de 0x20) et un "\n"
à la fin. - Le chef "pack" vous le préparera, à la minute, lorsque vous sélectionnerez le pack
code "u" dans le menu :
mon $uubuf = pack( 'u', $bindat );
Un nombre de répétitions après "u" définit le nombre d'octets à mettre dans une ligne codée uu, qui est
le maximum de 45 par défaut, mais pourrait être défini sur un multiple entier (plus petit) de
Trois. "unpack" ignore simplement le nombre de répétitions.
Faire Les sommes
Un code de modèle encore plus étrange est "%"nombre>. Premièrement, parce qu'il est utilisé comme préfixe pour
un autre code de modèle. Deuxièmement, parce qu'il ne peut pas du tout être utilisé dans "pack", et troisièmement,
dans "unpack", ne renvoie pas les données telles que définies par le code de modèle qu'il précède. Au lieu
cela vous donnera un nombre entier de nombre bits qui sont calculés à partir de la valeur des données en faisant
sommes. Pour les codes de déballage numériques, aucun exploit n'est réalisé :
mon $buf = pack( 'iii', 100, 20, 3 );
print unpack( '%32i3', $buf ), "\n"; # impressions 123
Pour les valeurs de chaîne, "%" renvoie la somme des valeurs d'octets, ce qui vous évite d'avoir à faire une somme
boucle avec "substr" et "ord":
print unpack( '%32A*', "\x01\x10" ), "\n"; # tirages 17
Bien que le code "%" soit documenté comme renvoyant une "somme de contrôle": ne faites pas confiance à
de telles valeurs ! Même appliqués à un petit nombre d'octets, ils ne garantissent pas un
distance de Hamming notable.
En relation avec "b" ou "B", "%" ajoute simplement des bits, et cela peut être utilisé à bon escient pour
compter les bits définis efficacement :
my $bitcount = unpack( '%32b*', $mask );
Et un bit de parité pair peut être déterminé comme ceci :
my $evenparity = unpack( '%1b*', $mask );
Unicode
Unicode est un jeu de caractères qui peut représenter la plupart des caractères dans la plupart des
langues, offrant de la place pour plus d'un million de caractères différents. Unicode 3.1 spécifie
94,140 0 caractères : les caractères latins de base sont affectés aux nombres 127 - XNUMX.
Le supplément Latin-1 avec des caractères qui sont utilisés dans plusieurs langues européennes est dans le
plage suivante, jusqu'à 255. Après quelques extensions latines supplémentaires, nous trouvons les jeux de caractères de
langues utilisant des alphabets non romains, entrecoupés d'une variété de jeux de symboles tels que
symboles monétaires, Zapf Dingbats ou Braille. (Vous voudrez peut-être visiter
<http://www.unicode.org/> pour un coup d'oeil à certains d'entre eux - mes favoris personnels sont Telugu
et Kannada.)
Les jeux de caractères Unicode associent des caractères à des entiers. Encoder ces nombres dans
un nombre égal d'octets ferait plus que doubler les exigences de stockage des textes écrits
en alphabet latin. L'encodage UTF-8 évite cela en stockant les plus courants (d'un
point de vue occidental) caractères dans un seul octet tout en codant les plus rares dans trois
ou plusieurs octets.
Perl utilise UTF-8, en interne, pour la plupart des chaînes Unicode.
Alors qu'est-ce que cela a à voir avec le "pack" ? Eh bien, si vous voulez composer une chaîne Unicode
(qui est codé en interne en UTF-8), vous pouvez le faire en utilisant le code de modèle "U". En tant que
exemple, produisons le symbole monétaire de l'euro (numéro de code 0x20AC) :
$UTF8{Euro} = pack( 'U', 0x20AC );
# Équivalent à : $UTF8{Euro} = "\x{20ac}" ;
L'inspection de $UTF8{Euro} montre qu'il contient 3 octets : "\xe2\x82\xac". Cependant, il
contient seulement 1 caractère, le nombre 0x20AC. L'aller-retour peut être complété par "unpack":
$Unicode{Euro} = unpack( 'U', $UTF8{Euro} );
Le déballage à l'aide du code de modèle "U" fonctionne également sur les chaînes d'octets encodées en UTF-8.
Habituellement, vous voudrez compresser ou décompresser les chaînes UTF-8 :
# emballer et déballer l'alphabet hébreu
mon $alefbet = pack( 'U*', 0x05d0..0x05ea );
my @hebrew = unpack( 'U*', $utf );
Attention : dans le cas général, il vaut mieux utiliser Encode::decode_utf8 pour décoder un
Chaîne d'octets encodée UTF-8 en une chaîne Unicode Perl et Encode::encode_utf8 pour encoder un
Chaîne Unicode Perl en octets UTF-8. Ces fonctions fournissent des moyens de gérer les octets invalides
séquences et ont généralement une interface plus conviviale.
Une autre Portable binaire Codage
Le code pack "w" a été ajouté pour prendre en charge un schéma de codage de données binaires portable qui
va bien au-delà des simples entiers. (Les détails peuvent être trouvés surhttp://Casbah.org/>, le Scarabée
projet.) Une base de stockage d'entiers non signés compressés BER (Binary Encoded Representation)
128 chiffres, le chiffre le plus significatif en premier, avec le moins de chiffres possible. Bit huit (le
bit de poids fort) est défini sur chaque octet sauf le dernier. Il n'y a pas de limite de taille pour l'encodage BER, mais
Perl n'ira pas aux extrêmes.
mon $berbuf = pack( 'w*', 1, 128, 128+1, 128*128+127 );
Un dump hexadécimal de $berbuf, avec des espaces insérés aux bons endroits, affiche 01 8100 8101
81807F. Comme le dernier octet est toujours inférieur à 128, "unpack" sait où s'arrêter.
Modèle regroupement
Avant Perl 5.8, les répétitions de modèles devaient être faites par "x"-multiplication de
chaînes de modèle. Il existe maintenant un meilleur moyen car nous pouvons utiliser les codes de pack "(" et ")"
combiné avec un compte de répétition. Le modèle "unpack" de l'exemple Stack Frame peut
s'écrit simplement comme ceci :
unpack( 'v2 (vXXCC)5 v5', $frame )
Explorons un peu plus cette fonctionnalité. Commençons par l'équivalent de
join( '', map( substr( $_, 0, 1 ), @str ) )
qui renvoie une chaîne constituée du premier caractère de chaque chaîne. En utilisant pack, nous
peut écrire
pack( '(A)'.@str, @str )
ou, parce qu'un nombre de répétitions "*" signifie "répéter aussi souvent que nécessaire", simplement
pack( '(A)*', @str )
(Notez que le modèle "A*" n'aurait emballé que $str[0] en entier.)
Pour emballer les dates stockées sous forme de triplets (jour, mois, année) dans un tableau @dates dans une séquence
d'octet, octet, entier court que nous pouvons écrire
$pd = pack( '(CCS)*', map( @$_, @dates ) );
Pour échanger des paires de caractères dans une chaîne (de longueur paire), on peut utiliser plusieurs
technique. Tout d'abord, utilisons "x" et "X" pour avancer et reculer :
$s = pack( '(A)*', unpack( '(xAXXAx)*', $s ) );
Nous pouvons également utiliser "@" pour sauter à un décalage, 0 étant la position où nous étions lorsque le
le dernier "(" a été rencontré :
$s = pack( '(A)*', unpack( '(@1A @0A @2)*', $s ) );
Enfin, il existe également une approche totalement différente en déballant les shorts big endian et
les emballer dans l'ordre inverse des octets :
$s = pack( '(v)*', unpack( '(n)*', $s );
Des longueurs et Largeurs
Chaîne Des longueurs
Dans la section précédente, nous avons vu un message réseau qui a été construit en préfixant le
longueur du message binaire au message réel. Vous constaterez que l'emballage d'une longueur suivie de
autant d'octets de données est une recette fréquemment utilisée car l'ajout d'un octet nul ne fonctionnera pas
si un octet nul peut faire partie des données. Voici un exemple où les deux techniques sont utilisées :
après deux chaînes terminées par zéro avec l'adresse source et de destination, un message court (à
un téléphone portable) est envoyé après un octet de longueur :
my $msg = pack( 'Z*Z*CA*', $src, $dst, length( $sm ), $sm );
Le déballage de ce message peut être effectué avec le même modèle :
( $src, $dst, $len, $sm ) = unpack( 'Z*Z*CA*', $msg );
Il y a un piège subtil qui se profile : ajouter un autre champ après le message court
(dans la variable $sm) est correct lors de l'emballage, mais cela ne peut pas être décompressé naïvement :
# emballer un message
my $msg = pack( 'Z*Z*CA*C', $src, $dst, length( $sm ), $sm, $prio );
# unpack échoue - $prio reste indéfini !
( $src, $dst, $len, $sm, $prio ) = unpack( 'Z*Z*CA*C', $msg );
Le code pack "A*" engloutit tous les octets restants, et $prio reste indéfini ! Avant nous
laissez la déception refroidir le moral : Perl a l'atout pour faire ce tour aussi,
juste un peu plus haut dans la manche. Regarde ça:
# emballer un message : ASCIIZ, ASCIIZ, longueur/chaîne, octet
mon $msg = pack( 'Z* Z* C/A* C', $src, $dst, $sm, $prio );
# déballer
( $src, $dst, $sm, $prio ) = unpack( 'Z* Z* C/A* C', $msg );
La combinaison de deux codes de pack avec une barre oblique ("/") les associe à une seule valeur de la
liste d'arguments. Dans "pack", la longueur de l'argument est prise et emballée selon le
premier code tandis que l'argument lui-même est ajouté après avoir été converti avec le code du modèle
après la barre oblique. Cela nous évite d'avoir à insérer l'appel "length", mais c'est en
"unpack" où nous marquons vraiment : La valeur de l'octet de longueur marque la fin de la chaîne
à retirer du tampon. Étant donné que cette combinaison n'a de sens que lorsque le
le code du deuxième pack n'est pas "a*", "A*" ou "Z*", Perl ne vous le permet pas.
Le code de pack précédant "/" peut être tout ce qui est apte à représenter un nombre : tous les
des codes de pack binaires numériques, et même des codes de texte tels que « A4 » ou « Z* » :
# compresser/décompresser une chaîne précédée de sa longueur en ASCII
my $buf = pack( 'A4/A*', "Humpty-Dumpty" );
# décompresser $buf : '13 Humpty-Dumpty'
mon $txt = unpack( 'A4/A*', $buf );
"/" n'est pas implémenté dans Perls avant 5.6, donc si votre code doit fonctionner sur des versions plus anciennes
Perls, vous devrez "décompresser ('Z* Z* C')" pour obtenir la longueur, puis l'utiliser pour créer un nouveau
décompressez la chaîne. Par exemple
# emballer un message : ASCIIZ, ASCIIZ, longueur, chaîne, octet
# (compatible 5.005)
my $msg = pack( 'Z* Z* CA* C', $src, $dst, length $sm, $sm, $prio );
# déballer
( undef, undef, $len) = unpack( 'Z* Z* C', $msg );
($src, $dst, $sm, $prio) = unpack ( "Z* Z* x A$len C", $msg );
Mais ce deuxième "déballage" se précipite. Il n'utilise pas une simple chaîne littérale pour le
modèle. Alors peut-être devrions-nous présenter...
Dynamique Gabarits
Jusqu'à présent, nous avons vu des littéraux utilisés comme modèles. Si la liste des éléments du pack n'a pas
longueur fixe, une expression construisant le modèle est requise (lorsque, pour certains
raison, "()*" ne peut pas être utilisé). Voici un exemple : pour stocker des valeurs de chaîne nommées d'une manière
qui peut être facilement analysé par un programme C, nous créons une séquence de noms et null
chaînes ASCII terminées, avec "=" entre le nom et la valeur, suivi d'un
octet nul de délimitation supplémentaire. Voici comment:
my $env = pack( '(A*A*Z*)' . keys( %Env ) . 'C',
map( { ( $_, '=', $Env{$_} ) } keys( %Env ) ), 0 );
Examinons les rouages de ce moulin à octets, un par un. Il y a l'appel "map", créant le
éléments que nous avons l'intention de mettre dans le tampon $env : à chaque clé (dans $_) il ajoute le "="
séparateur et la valeur d'entrée de hachage. Chaque triplet est emballé avec le code du modèle
séquence "A*A*Z*" qui se répète en fonction du nombre de touches. (Oui, c'est ce que le
La fonction "keys" renvoie dans un contexte scalaire.) Pour obtenir le tout dernier octet nul, nous ajoutons un 0 à
la fin de la liste "pack", à emballer avec "C". (Les lecteurs attentifs ont peut-être remarqué
que nous aurions pu omettre le 0.)
Pour l'opération inverse, il va falloir déterminer le nombre d'éléments dans le buffer
avant de laisser "unpack" le déchirer :
mon $n = $env =~ tr/\0// - 1;
my %env = map( split( /=/, $_ ), unpack( "(Z*)$n", $env ) );
Le "tr" compte les octets nuls. L'appel "unpack" renvoie une liste de paires nom-valeur chacune
dont est démonté dans le bloc "map".
Compte Répétitions
Plutôt que de stocker une sentinelle à la fin d'une donnée (ou d'une liste d'éléments), on pourrait
faire précéder les données d'un décompte. Encore une fois, nous emballons les clés et les valeurs d'un hachage, précédant chaque
avec un compte de longueur courte non signé, et à l'avance, nous stockons le nombre de paires :
my $env = pack( 'S(S/A* S/A*)*', clés scalaires( %Env ), %Env );
Cela simplifie l'opération inverse car le nombre de répétitions peut être décompressé avec
le code:
mon %env = unpack( 'S/(S/A* S/A*)', $env );
Notez que c'est l'un des rares cas où vous ne pouvez pas utiliser le même modèle pour "pack"
et "unpack" car "pack" ne peut pas déterminer un nombre de répétitions pour un groupe "()".
Intel HEX
Intel HEX est un format de fichier pour représenter des données binaires, principalement pour programmer divers
puces, sous forme de fichier texte. (Voirhttp://en.wikipedia.org/wiki/.hex> pour un détail
descriptif, ethttp://en.wikipedia.org/wiki/SREC_(file_format)> pour le Motorola
format d'enregistrement S, qui peut être démêlé en utilisant la même technique.) Chaque ligne commence par
un deux-points (':') et est suivi d'une séquence de caractères hexadécimaux, spécifiant un octet
compter n (8 bits), une adresse (16 bits, big endian), un type d'enregistrement (8 bits), n octets de données et
une somme de contrôle (8 bits) calculée comme l'octet le moins significatif de la somme du complément à deux de
les octets précédents. Exemple : " :0300300002337A1E".
La première étape du traitement d'une telle ligne est la conversion, en binaire, de l'hexadécimal
data, pour obtenir les quatre champs, tout en vérifiant la somme de contrôle. Pas de surprise ici : nous allons
commencez par un simple appel "pack" pour tout convertir en binaire :
my $binrec = pack( 'H*', substr( $hexrec, 1 ) );
La séquence d'octets résultante est la plus pratique pour vérifier la somme de contrôle. Ne ralentissez pas votre
programme vers le bas avec une boucle for ajoutant les valeurs "ord" des octets de cette chaîne - le "unpack"
le code "%" est la chose à utiliser pour calculer la somme de 8 bits de tous les octets, qui doit être égale
à zéro :
mourir sauf si unpack( "%8C*", $binrec ) == 0;
Enfin, obtenons ces quatre champs. A présent, vous ne devriez pas avoir de problèmes avec le
trois premiers champs - mais comment pouvons-nous utiliser le nombre d'octets des données dans le premier champ comme
longueur pour le champ de données? Ici, les codes « x » et « X » viennent à la rescousse, car ils permettent
sauter d'avant en arrière dans la ficelle pour déballer.
my( $addr, $type, $data ) = unpack( "xn C X4 C x3 /a", $bin );
Le code "x" saute un octet, car nous n'avons pas encore besoin du décompte. Le code "n" prend en charge le
Adresse entière big-endian 16 bits et "C" décompresse le type d'enregistrement. Étant à l'offset 4,
là où les données commencent, nous avons besoin du décompte. "X4" nous ramène à la case départ, qui est le
octet au décalage 0. Maintenant, nous reprenons le compte et zoomons sur le décalage 4, où nous sommes maintenant
entièrement fourni pour extraire le nombre exact d'octets de données, laissant la somme de contrôle de fin
octet seul.
Emballage et Déballage C Structure
Dans les sections précédentes, nous avons vu comment compresser des nombres et des chaînes de caractères. Si c'était
pas pour quelques accrocs, nous pourrions conclure cette section tout de suite avec la remarque laconique
que les structures C ne contiennent rien d'autre, et donc vous savez déjà tout ce qu'il y a
à cela. Désolé, non : continuez à lire, s'il vous plaît.
Si vous devez gérer beaucoup de structures C et que vous ne voulez pas pirater tout votre modèle
chaînes manuellement, vous voudrez probablement jeter un œil au module CPAN
"Convertir::Binaire::C". Non seulement il peut analyser votre source C directement, mais il a également intégré
à l'appui de toutes les bricoles décrites plus loin dans cette section.
Le manuel de formation Alignement Fosse
Dans la considération de la vitesse par rapport aux exigences de mémoire, la balance a été inclinée dans
faveur d'une exécution plus rapide. Cela a influencé la façon dont les compilateurs C allouent de la mémoire pour
structures : Sur les architectures où un opérande 16 bits ou 32 bits peut être déplacé plus rapidement entre
emplacements en mémoire, ou vers ou depuis un registre CPU, s'il est aligné sur un nombre pair ou multiple.
de quatre ou même à une adresse multiple de huit, un compilateur C vous donnera cette vitesse
bénéficier en bourrant des octets supplémentaires dans les structures. Si vous ne traversez pas le rivage C, ce
n'est pas susceptible de vous causer de la peine (bien que vous deviez faire attention lorsque vous concevez des données volumineuses
structures, ou vous voulez que votre code soit portable entre les architectures (vous le voulez,
n'est-ce pas ?)).
Pour voir comment cela affecte "pack" et "unpack", nous allons comparer ces deux structures C :
typedef struct {
char c1;
shorts;
char c2;
long je;
} gappy_t ;
typedef struct {
long je;
shorts;
char c1;
char c2;
} dense_t ;
Typiquement, un compilateur C alloue 12 octets à une variable "gappy_t", mais n'en requiert que 8
octets pour un "dense_t". Après avoir étudié cela plus en détail, nous pouvons dessiner des cartes de mémoire, montrant
où les 4 octets supplémentaires sont cachés :
0 +4 +8 +12
+--+--+--+--+--+--+--+--+--+--+--+--+
|c1|xx| s |c2|xx|xx|xx| l | xx = remplir l'octet
+--+--+--+--+--+--+--+--+--+--+--+--+
Gappy_t
0 +4 +8
+--+--+--+--+--+--+--+--+
| l | h |c1|c2|
+--+--+--+--+--+--+--+--+
dense_t
Et c'est là que la première bizarrerie frappe : les modèles "pack" et "unpack" doivent être bourrés
avec des codes "x" pour obtenir ces octets de remplissage supplémentaires.
La question naturelle : « Pourquoi Perl ne peut-il pas compenser les lacunes ? mérite une réponse. Une
une bonne raison est que les compilateurs C peuvent fournir des extensions (non ANSI) permettant toutes sortes
de contrôle fantaisiste sur la façon dont les structures sont alignées, même au niveau d'un individu
champ structurel. Et, si cela ne suffisait pas, il y a une chose insidieuse qui s'appelle "l'union"
où la quantité d'octets de remplissage ne peut pas être dérivée de l'alignement de l'élément suivant
seul.
OK, alors mords la balle. Voici une façon d'obtenir le bon alignement en insérant
codes de modèle "x", qui ne prennent pas un élément correspondant de la liste :
my $gappy = pack( 'cxs cxxx l!', $c1, $s, $c2, $l );
Noter la "!" après "l": nous voulons nous assurer que nous compressons un entier long au fur et à mesure qu'il est compilé
par notre compilateur C. Et même maintenant, cela ne fonctionnera que pour les plates-formes sur lesquelles le compilateur
aligne les choses comme ci-dessus. Et quelqu'un quelque part a une plate-forme là où il n'y en a pas.
[Probablement un Cray, où les "short", "int" et "long" sont tous de 8 octets. :-)]
Compter les octets et surveiller les alignements dans de longues structures est forcément un frein. N'est-ce pas
existe-t-il un moyen de créer le modèle avec un programme simple ? Voici un programme C qui fait
l'astuce:
#inclut
#comprendre
typedef struct {
caractère fc1;
fs court;
caractère fc2;
long fl;
} 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" );
}
La ligne de sortie peut être utilisée comme modèle dans un appel « pack » ou « unpack » :
my $gappy = pack( '@0c @2s! @4c @8l!', $c1, $s, $c2, $l );
Eh bien, encore un autre code de modèle - comme si nous n'en avions pas beaucoup. Mais "@" sauve notre journée en permettant
nous pour spécifier le décalage entre le début du tampon de pack et l'élément suivant : c'est
juste la valeur de la macro "offsetof" (définie dans " ") retourne lorsqu'on lui donne un
type "struct" et l'un de ses noms de champs ("member-designator" en C standard).
Ni l'utilisation de décalages ni l'ajout de "x" pour combler les lacunes n'est satisfaisant. (Imagine seulement
ce qui se passe si la structure change.) Ce dont nous avons vraiment besoin, c'est d'une façon de dire
autant d'octets que nécessaire jusqu'au prochain multiple de N". Dans un Templatese fluide, vous dites ceci
avec "x!N" où N est remplacé par la valeur appropriée. Voici la prochaine version de notre
emballage de structure :
my $gappy = pack( 'cx!2 scx!4 l!', $c1, $s, $c2, $l );
C'est certainement mieux, mais encore faut-il savoir combien de temps sont tous les entiers, et
la portabilité est loin. Plutôt que 2, par exemple, nous voulons dire « quelle que soit la durée
est". Mais cela peut être fait en mettant le code de pack approprié entre parenthèses : "[s]". Ainsi,
voici ce que nous pouvons faire de mieux :
my $gappy = pack( 'cx![s] scx![l!] l!', $c1, $s, $c2, $l );
Transaction avec Endianité
Maintenant, imaginez que nous voulons compresser les données d'une machine avec un ordre d'octet différent.
Tout d'abord, nous devrons déterminer la taille réelle des types de données sur la machine cible.
Supposons que les longs aient une largeur de 32 bits et que les courts aient une largeur de 16 bits. Vous pouvez alors
réécrivez le modèle comme :
my $gappy = pack( 'cx![s] scx![l] l', $c1, $s, $c2, $l );
Si la machine cible est little-endian, on pourrait écrire :
my $gappy = pack( 'cx![s] s< cx![l] l<', $c1, $s, $c2, $l );
Cela oblige les membres courts et longs à être little-endian, et c'est très bien si vous
n'ayez pas trop de membres de structure. Mais nous pourrions également utiliser le modificateur d'ordre d'octet sur un
groupe et écrivez ce qui suit :
my $gappy = pack( '( cx![s] scx![l] l )<', $c1, $s, $c2, $l );
Ce n'est pas aussi court qu'avant, mais cela montre plus clairement que nous avons l'intention d'avoir
ordre des octets little-endian pour un groupe entier, pas seulement pour les codes de modèle individuels. Ça peut
également être plus lisible et plus facile à entretenir.
Alignement, Prenez 2
J'ai bien peur que nous n'en ayons pas encore fini avec le rattrapage d'alignement. L'hydre s'élève
une autre tête laide lorsque vous emballez des tableaux de structures :
typedef struct {
compte court;
glyphe de caractère ;
} cellule_t ;
typedef cell_t buffer_t[BUFLEN] ;
Où est le piège ? Le remplissage n'est requis ni avant le premier champ "compte", ni entre
ceci et le champ suivant "glyphe", alors pourquoi ne pouvons-nous pas simplement compresser comme ceci :
# quelque chose ne va pas ici :
pack( 's!a' x @buffer,
map{ ( $_->{count}, $_->{glyph} ) } @buffer );
Cela emballe "3*@buffer" octets, mais il s'avère que la taille de "buffer_t" est quatre fois supérieure
« BUFLEN » ! La morale de l'histoire est que l'alignement requis d'une structure ou d'un réseau est
propagé au niveau supérieur suivant où nous devons considérer le remplissage at le fin de chaque
composant également. Ainsi, le modèle correct est :
pack( 's!ax' x @buffer,
map{ ( $_->{count}, $_->{glyph} ) } @buffer );
Alignement, Prenez 3
Et même si vous tenez compte de tout ce qui précède, l'ANSI permet toujours ceci :
typedef struct {
char foo[2] ;
} pied;
varient en taille. La contrainte d'alignement de la structure peut être supérieure à n'importe laquelle de ses
éléments. [Et si vous pensez que cela n'affecte rien de commun, démembrez le prochain
téléphone portable que vous voyez. Beaucoup ont des cœurs ARM, et les règles de structure ARM font "sizeof
(pied_t)" == 4]
Pointers pour Comment la à Utilisez le Les
Le titre de cette section indique le deuxième problème que vous pourriez rencontrer tôt ou tard
lorsque vous emballez des structures C. Si la fonction que vous avez l'intention d'appeler attend un, disons, "void *"
valeur, vous ne peut pas prenez simplement une référence à une variable Perl. (Bien que cette valeur
est certainement une adresse mémoire, ce n'est pas l'adresse où se trouve le contenu de la variable
stocké.)
Le code de modèle "P" promet d'emballer un "pointeur vers une chaîne de longueur fixe". N'est-ce pas ce que
nous voulons? Essayons:
# allouez de l'espace de stockage et placez un pointeur dessus
ma $memory = "\x00" x $size ;
my $memptr = pack( 'P', $memory );
Mais attendez : "pack" ne renvoie-t-il pas simplement une séquence d'octets ? Comment pouvons-nous passer cette chaîne de
octets à un code C attendant un pointeur qui n'est, après tout, rien d'autre qu'un nombre ? Les
la réponse est simple : nous devons obtenir l'adresse numérique à partir des octets renvoyés par "pack".
my $ptr = unpack( 'L!', $memptr );
Évidemment, cela suppose qu'il est possible de transtyper un pointeur vers un texte long et non signé.
vice versa, qui fonctionne fréquemment mais ne doit pas être considérée comme une loi universelle. - Maintenant que
nous avons ce pointeur, la question suivante est : comment pouvons-nous en faire bon usage ? Nous avons besoin d'un appel
à une fonction C où un pointeur est attendu. Les lire(2) l'appel système me vient à l'esprit :
ssize_t read(int fd, void *buf, size_t count);
Après avoir lu perlfunc expliquant comment utiliser "syscall", nous pouvons écrire cette fonction Perl
copier un fichier sur la sortie standard :
nécessite 'syscall.ph'; # lancez h2ph pour générer ce fichier
sous-chat($){
mon $chemin = shift();
my $size = -s $path ;
ma $memory = "\x00" x $size ; # allouer de la mémoire
my $ptr = unpack( 'L', pack( 'P', $memory ) );
open( F, $chemin ) || die( "$path : impossible d'ouvrir ($!)\n" );
mon $fd = fileno(F);
my $res = syscall( &SYS_read, fileno(F), $ptr, $size );
imprimer $memory ;
fermer( F );
}
Ce n'est ni un exemple de simplicité ni un modèle de portabilité, mais il illustre
le point : nous pouvons nous faufiler dans les coulisses et accéder à Perl par ailleurs bien gardé
Mémoire! (Remarque importante : le « syscall » de Perl ne pas vous obliger à construire des pointeurs dans
ce chemin détourné. Vous transmettez simplement une variable de chaîne et Perl transfère l'adresse.)
Comment fonctionne "unpack" avec "P" ? Imaginez un pointeur dans le tampon sur le point d'être décompressé :
Si ce n'est pas le pointeur nul (qui produira intelligemment la valeur "undef"), nous avons un
adresse de départ - mais alors quoi? Perl n'a aucun moyen de savoir combien de temps cette "longueur fixe
string" est, c'est donc à vous de spécifier la taille réelle en tant que longueur explicite après "P".
mon $mem = "abcdefghijklmn" ;
print unpack( 'P5', pack( 'P', $mem ) ); # imprime "abcde"
Par conséquent, "pack" ignore tout nombre ou "*" après "P".
Maintenant que nous avons vu "P" à l'œuvre, autant faire tourner "p". Pourquoi avons-nous besoin d'un
deuxième modèle de code pour emballer des pointeurs? La réponse se cache derrière le simple fait
qu'un "unpack" avec "p" promet une chaîne terminée par un zéro commençant à l'adresse prise
du buffer, et cela implique une longueur pour la donnée à renvoyer :
mon $buf = pack( 'p', "abc\x00efhijklmn" );
print unpack( 'p', $buf ); # imprime "abc"
Bien que cela puisse prêter à confusion : en raison de la longueur impliquée par le
la longueur de la chaîne, un nombre après le code de pack "p" est un nombre de répétitions, pas une longueur comme après
"P".
L'utilisation de "pack(..., $x)" avec "P" ou "p" pour obtenir l'adresse où $x est réellement stocké doit
être utilisé avec circonspection. La machinerie interne de Perl considère la relation entre un
variable et cette adresse comme sa propre affaire privée et ne se soucie pas vraiment que nous
avoir obtenu une copie. Par conséquent:
· Ne pas utiliser "pack" avec "p" ou "P" pour obtenir l'adresse de la variable qui doit aller
hors de portée (et libérant ainsi sa mémoire) avant d'avoir fini d'utiliser le
mémoire à cette adresse.
· Soyez très prudent avec les opérations Perl qui modifient la valeur de la variable. Ajout
quelque chose à la variable, par exemple, peut nécessiter une réaffectation de son stockage,
vous laissant avec un pointeur dans le no man's land.
· Ne pensez pas que vous pouvez obtenir l'adresse d'une variable Perl lorsqu'elle est stockée en tant que
nombre entier ou double ! "pack('P', $x)" forcera la variable interne
représentation en chaîne, comme si vous aviez écrit quelque chose comme "$x .= ''".
Cependant, il est sûr de compresser en P ou en p un littéral de chaîne, car Perl alloue simplement un
variable anonyme.
PACK Recettes
Voici une collection de recettes en conserve (éventuellement) utiles pour « emballer » et « déballer » :
# Convertir l'adresse IP pour les fonctions de socket
pack( "C4", diviser /\./, "123.4.5.6" );
# Compter les bits dans un morceau de mémoire (par exemple un vecteur de sélection)
unpack( '%32b*', $mask );
# Déterminez l'endianité de votre système
$is_little_endian = unpack( 'c', pack( 's', 1 ) );
$is_big_endian = unpack( 'xc', pack( 's', 1 ) );
# Déterminer le nombre de bits dans un entier natif
$bits = unpack( '%32I!', ~0 );
# Préparer l'argument pour l'appel système nanosleep
my $timespec = pack( 'L!L!', $secs, $nanosecs );
Pour un simple vidage de mémoire, nous décompressons quelques octets en autant de paires de chiffres hexadécimaux, et
utilisez "map" pour gérer l'espacement traditionnel - 16 octets par ligne :
mon $i ;
print map( ++$i % 16 ? "$_ " : "$_\n",
unpack( 'H2' x longueur( $mem ), $mem ) ),
longueur( $mem ) % 16 ? "\n" : '';
Marrant Blog
# Sortir des chiffres de nulle part...
print unpack( 'C', pack( 'x' ) ),
unpack( '%B*', pack( 'A' ) ),
unpack( 'H', pack( 'A' ) ),
unpack( 'A', unpack( 'C', pack( 'A' ) ) ), "\n";
# Un pour la route ;-)
my $advice = pack( 'all u can in a van' );
Auteurs
Simon Cozens et Wolfgang Laun.
Utilisez perlpacktut en ligne en utilisant les services onworks.net