Τοποθέτηση και συμπλήρωση#

Στο τέλος αυτού του κεφαλαίου θα μπορείτε να παρακάμπτετε bytes, να εισάγετε συμπλήρωση, να μεταπηδάτε σε απόλυτα offsets, και να ευθυγραμμίζετε πεδία σε αυθαίρετα όρια - οι τέσσερις οδηγίες x, X, @, και ..

Ορισμένες οδηγίες παράγουν ένα byte χωρίς να καταναλώνουν τιμή από τη λίστα (bytes συμπλήρωσης). Ορισμένες καταναλώνουν μια τιμή χωρίς να παράγουν byte (εντολές τοποθέτησης). Μαζί σας επιτρέπουν να μοντελοποιείτε τα κενά και τις ευθυγραμμίσεις που περιέχουν τα πραγματικά δυαδικά πρότυπα.

x - παράλειψη προς τα εμπρός / εισαγωγή NUL#

Το x είναι το απλούστερο από τα τέσσερα. Στην pack εισάγει ένα byte NUL χωρίς να καταναλώνει τιμή· στην unpack παρακάμπτει ένα byte χωρίς να παράγει τιμή.

pack   "C x C", 65, 66       # "A\0B"
unpack "C x C", "A\0B"       # (65, 66)

Με μετρητή επανάληψης, εισάγει ή παρακάμπτει τόσα bytes:

pack "C x3 C", 65, 66        # "A\0\0\0B"

Το x* σημαίνει «παράλειψη μέχρι το τέλος της συμβολοσειράς» στην unpack, και ισοδυναμεί με x0 (no-op) στην pack.

Το x δεν σημαίνει «ένα κενό»#

Το μπερδεμένο με το x είναι ότι συμπληρώνει με NUL, όχι με κενό. Αν θέλετε συμπλήρωση με κενά για κείμενο, χρησιμοποιήστε το A με ευρύτερο μετρητή επανάληψης:

# Wrong - Perl inserts a NUL, not a space
pack "A10 x A10", "date", "label"
# "date      \0label     "

# Right - widen the field
pack "A11 A10", "date", "label"
# "date       label     "

X - επιστροφή προς τα πίσω#

Το X μετακινεί την κεφαλή εγγραφής/ανάγνωσης προς τα πίσω κατά ένα byte. Με μετρητή επανάληψης, προς τα πίσω κατά τόσα bytes. Σε αντίθεση με το x, το X δεν έχει επίδραση στα bytes εξόδου της pack - απλώς επανατυλίσσει ώστε μια επόμενη οδηγία να τα αντικαταστήσει.

pack "CC X C", 65, 66, 67    # "AC" - the second C overwrites 66 with 67

Το X είναι κυρίως χρήσιμο με την unpack: ματιά μπροστά, μετά επανατύλιξη για επανανάγνωση:

# Read a 16-bit length, then re-read the same 2 bytes as two separate bytes
my ($len, $hi, $lo) = unpack "n XX CC", $frame;

Το X πριν την αρχή της συμβολοσειράς είναι μοιραίο σφάλμα.

@ - μετάβαση σε απόλυτη θέση#

Το @N ορίζει την τρέχουσα θέση στο byte N εντός της εσώτατης ομάδας () (ή από την αρχή της συμβολοσειράς αν δεν υπάρχει περικλείουσα ομάδα). Στην pack, αυτό συμπληρώνει με μηδενικά από την τρέχουσα θέση προς τα εμπρός, ή αποκόπτει αν η τρέχουσα θέση είναι μετά το N:

pack "C @4 C", 65, 66        # "A\0\0\0B"  - byte 0 is 'A', byte 4 is 'B'

Αυτό είναι το αντίστοιχο του offsetof(struct, field) σε επίπεδο προτύπου: αν ένα header αρχείου C σας πει ότι το πεδίο 2 βρίσκεται στο offset 12, το @12 σας παρκάρει εκεί ανεξάρτητα από τι έκαναν οι προηγούμενες οδηγίες.

Το @ μέσα σε ομάδα#

Σε κάθε επανάληψη μιας ομάδας, το @ επανεκκινεί στο 0. Αυτό μπορεί να σας εκπλήξει:

pack '@1A((@2A)@3A)', qw(X Y Z)
# "\0X\0\0YZ"

Βήμα προς βήμα: το εξωτερικό @1A βάζει "X" στο byte 1 (με NUL στο byte 0). Η εσωτερική ομάδα ξεκινάει νέο σύστημα συντεταγμένων στη θέση του "X"· μέσα της, το @2A βάζει "Y" στο byte 2 της ομάδας (με δύο ακόμα NUL ενδιάμεσα), μετά το @3A βάζει "Z" στο byte 3 της ομάδας. Διαβάστε τις προδιαγραφές προσεκτικά πριν χρησιμοποιήσετε @ μέσα σε ομάδες.

. - απόλυτη θέση από τα δεδομένα#

Το . είναι ο εξάδελφος του @ που οδηγείται από τιμές. Αντί για σταθερό μετρητή επανάληψης, η θέση προέρχεται από την επόμενη τιμή στη λίστα:

pack "C . C", 65, 4, 66     # "A\0\0\0B" - position 4 from the next value

Χρήσιμο όταν το offset υπολογίζεται κατά τον χρόνο εκτέλεσης - ας πούμε, από ένα πεδίο header που μόλις διαβάσατε:

my $buf = pack "C .N C", $type, $offset, $value;
# $offset bytes of NUL fill before $value

Στην unpack, το . επιστρέφει την τρέχουσα θέση αντί να συμπληρώνει με μηδενικά:

my ($a, $pos) = unpack "C .", "hello";
# $a = 104 ('h'), $pos = 1

Με .*, το offset μετριέται από την αρχή ολόκληρης της συμβολοσειράς· με .N για ακέραιο N 1, από την αρχή της περικλείουσας N-οστής ομάδας· με .0, σχετικά με την τρέχουσα θέση.

Ο τροποποιητής !: ευθυγράμμιση#

Τα x!N και X!N μετατρέπουν τα x και X σε εντολές ευθυγράμμισης: προωθούν (ή επανατυλίσσουν) στο πλησιέστερο πολλαπλάσιο του N. Έτσι σέβεστε τους κανόνες συμπλήρωσης struct της C.

# struct { char c; double d; char cc[2]; }
my $s = pack "c x![d] d c2", $c, $d, $c1, $c2;

Διαβάζοντας το x![d]: «ευθυγράμμιση προς τα εμπρός σε πολλαπλάσιο του πλάτους που θα έπαιρνε ένα πακεταρισμένο d» - δηλαδή 8 bytes. Αυτός είναι ακριβώς ο κανόνας που εφαρμόζουν οι περισσότεροι μεταγλωττιστές C σε ένα πεδίο double μετά από ένα char. Χρησιμοποιήστε [l!], [d], [Q] για να ονομάσετε την απαίτηση ευθυγράμμισης χωρίς να χρειάζεται να γνωρίζετε το αριθμητικό πλάτος.

Ένα γυμνό x!0 ή x!1 είναι no-op.

Αθροίσματα ελέγχου - %N μόνο στην unpack#

Το %N είναι πρόθεμα σε μία οδηγία, όχι αυτοτελής οδηγία. Λέει «αντί να επιστραφούν οι τιμές που θα παρήγαγε η επόμενη οδηγία, επίστρεψε το N-bit άθροισμά τους». Συνηθισμένες χρήσεις:

my $bytesum = unpack "%32C*", $buf;    # sum every byte
my $setbits = unpack "%32b*", $mask;   # count set bits

Υπάρχει μόνο στην unpack. Δεν υπάρχει % στην pack - δεν μπορείτε να πακετάρετε άθροισμα ελέγχου απευθείας· υπολογίστε το οι ίδιοι και πακετάρετε το αποτέλεσμα με όποια ακέραια οδηγία χρειάζεστε.

Επεξεργασμένο παράδειγμα: ανάγνωση φορτίου με μεταβλητό offset#

Ορισμένες μορφές ξεκινούν με header σταθερού μεγέθους αλλά τοποθετούν τα πραγματικά δεδομένα σε ένα offset που περιέχει το header. Η κλασική συνταγή:

# Header: magic (4 bytes), version (1 byte), data-offset (4 bytes BE),
# then data starts at the byte-offset the header pointed at.
my ($magic, $ver, $off) = unpack "A4 C N", $file;

die "bad magic" unless $magic eq "MAGI";

# Jump to the data section
my $data = unpack "x$off a*", $file;

Δύο ξεχωριστές κλήσεις unpack - η πρώτη διαβάζει το offset, η δεύτερη το χρησιμοποιεί στο πρότυπο. Οι συμβολοσειρές προτύπων δεν είναι κανονικές εκφράσεις· δεν έχουν τρόπο να κοιτάξουν μπροστά και να χρησιμοποιήσουν μια υπολογισμένη τιμή εντός της ίδιας κλήσης. Αποπακετάρετε σε στάδια όταν το σχήμα των δεδομένων εξαρτάται από αυτά που μόλις διαβάσατε.

Με την τοποθέτηση και την ευθυγράμμιση στην εργαλειοθήκη, τα δύο επεξεργασμένα παραδείγματα στα επόμενα κεφάλαια θα διαβάζονται ως απλές εφαρμογές: ανάλυση ενός πραγματικού header δικτύου και μιας πραγματικής μορφής αρχείου.