Ομαδοποίηση και μετρητές#
Στο τέλος αυτού του κεφαλαίου θα μπορείτε να επαναλαμβάνετε ένα μοτίβο οδηγιών σε μια λίστα τιμών, να χρησιμοποιείτε τις μορφές * και […] του μετρητή επανάληψης, και να γράφετε αυτο-περιγραφόμενες εγγραφές με πρόθεμα μήκους με την οδηγία /.
A bare repeat count applies to one directive letter: C4 packs four bytes. The moment the repeating unit is more than one directive
«pack a short and two bytes, many times» - you need a group.
() - μια ομάδα είναι ένα υπο-πρότυπο#
Οι παρενθέσεις συγκεντρώνουν μια ακολουθία οδηγιών ώστε ένας μετρητής επανάληψης ή τροποποιητής endianness να εφαρμόζεται στο σύνολο. Συγκρίνετε:
pack "C S C S C S", @a, @b, @c, @d, @e, @f # repeat by hand
pack "(CS)3", @a, @b, @c, @d, @e, @f # same thing, grouped
pack "(CS)*", @pairs # repeat as often as values last
Μια ομάδα δεν έχει δικό της κόστος σε bytes - είναι συντακτικός μηχανισμός. Το συνολικό πακεταρισμένο μήκος είναι αυτό που θα παρήγαγαν οι οδηγίες μέσα της χωρίς τις παρενθέσεις.
Ομαδοποίηση με endianness#
Η πιο πρακτική χρήση μιας ομάδας - μια υπο-δομή της οποίας κάθε ακέραιος μοιράζεται μία σειρά bytes:
my $rec = pack "(l s s)<", $id, $x, $y;
# same as "l<s<s<"
Το < διαχέεται σε κάθε οδηγία με σειρά bytes που βρίσκεται μέσα, συμπεριλαμβανομένων των εμφωλευμένων ομάδων. Οι οδηγίες που δεν δέχονται τροποποιητή σειράς bytes (όπως οι C, a, Z) μένουν σιωπηλά ανεπηρέαστες.
Μετρητές επανάληψης αναλυτικά#
Μετά από οποιαδήποτε οδηγία ή ομάδα, μπορείτε να γράψετε:
Μορφή | Σημασία |
|---|---|
| Εφαρμόστε την οδηγία/ομάδα |
| Εφαρμογή όσο διαρκούν οι τιμές. Για |
| Όπως το |
| Ο μετρητής επανάληψης είναι το μήκος σε πακεταρισμένα bytes του προτύπου σε αγκύλες |
Η μορφή [template] είναι το εργαλείο για να εκφράσετε «τόσα bytes όσα παίρνει ένα foo»:
pack "x[L]" # skip 4 bytes (sizeof a packed long)
pack "x[d]" # skip 8 bytes (sizeof a packed double)
pack "a[Q]" # one string 8 bytes wide
Είναι ιδιαίτερα χρήσιμη για ευθυγράμμιση (δείτε το κεφάλαιο για την τοποθέτηση) και για πρότυπα των οποίων τα πλάτη πρέπει να ακολουθούν το εξαρτώμενο από την πλατφόρμα μέγεθος ενός native ακεραίου:
pack "a[l!]", $native_long_buf # room for one native long
Το * εφαρμόζεται ανά ομάδα-τιμής, όχι «καταπίνει τα πάντα»#
Ένα μεμονωμένο * μετράει «υπόλοιπες τιμές» για αυτή την οδηγία. Δύο A* στη σειρά δεν ανταγωνίζονται:
pack "A*A*", "hello", "world" # "helloworld"
Το πρώτο A* πακετάρει όλο το "hello"· το δεύτερο πακετάρει όλο το "world". Κάθε * καταναλώνει μία τιμή από τη λίστα, στο πλήρες μήκος αυτής της τιμής. Αυτός είναι ο γενικός κανόνας: κάθε οδηγία αντιστοιχεί σε ένα κομμάτι δεδομένων, ανεξάρτητα από τον μετρητή επανάληψης.
Η οδηγία / - μήκος και δεδομένα μαζί#
Οι μορφές επί της γραμμής συχνά αποθηκεύουν ένα μετρητή ακριβώς πριν από το πράγμα που μετριέται: «ένα μήκος 2-byte, μετά τόσα bytes φορτίου». Η οδηγία / συνδέει τα δύο σε ένα βήμα.
Στην pack: length-item/sequence-item#
Γράψτε δύο οδηγίες χωρισμένες με κάθετο. Η πρώτη πακετάρει το μήκος· η δεύτερη πακετάρει το φορτίο. Η pack υπολογίζει το μήκος για εσάς:
my $msg = pack "n/a*", "hello, world";
# "\x00\x0chello, world"
# ^^^^^^^^ big-endian 16-bit length = 12
# ^^^^^^^^^^^ the payload itself
Το length-item μπορεί να είναι οποιαδήποτε αριθμητική οδηγία - C, n, N, w, S<, και ούτω καθεξής - ή οδηγία συμβολοσειράς όπως A4 όταν το πρωτόκολλο γράφει το μήκος ως ASCII:
my $buf = pack "A4/A*", "Humpty-Dumpty";
# "13 Humpty-Dumpty" - 4-char ASCII length, then the string
Στην unpack: /item#
Η μορφή της unpack είναι απλούστερη: ένα γυμνό / πριν από το στοιχείο. Ο μετρητής λαμβάνεται από την πιο πρόσφατη ακέραια οδηγία:
my ($payload) = unpack "n/a*", $msg; # "hello, world"
Διαβάζοντας αυτό το πρότυπο: «διάβασε έναν ακέραιο n, ονόμασέ τον L· μετά διάβασε L bytes ως συμβολοσειρά τύπου a*.» Το ίδιο το μήκος δεν εμφανίζεται στη λίστα εξόδου.
Το συνηθισμένο λάθος: A* μετά από /#
Δεν μπορείτε να βάλετε άλλο A* ή a* μετά από πεδίο που εισάγει / στην unpack και να περιμένετε ότι θα συμπεριφερθεί - το * είναι άπληστο:
# Wrong - $prio will be undef, $sm gets everything left
my ($src, $dst, $sm, $prio) = unpack "Z* Z* C A* C", $buf;
# Right - use /A* so $sm knows where to stop
my ($src, $dst, $sm, $prio) = unpack "Z* Z* C/A* C", $buf;
Στο δεύτερο πρότυπο, το C/A* διαβάζει έναν μετρητή bytes και μετά τόσα bytes. Όλα μετά την κάθετο τον σέβονται, και το τελικό C παίρνει το επόμενο byte όπως αναμενόταν.
Επεξεργασμένο παράδειγμα: ζεύγη κλειδιού-τιμής#
Ένα πρωτόκολλο αποθηκεύει ένα λεξικό ως count ακολουθούμενο από count ζεύγη της μορφής (length, key, length, value):
my %env = ( HOST => "example.com",
PORT => "443",
USER => "alice" );
my $blob = pack "S (S/A* S/A*)*",
scalar keys %env,
%env;
Διαβάζοντάς το πίσω:
my %parsed = unpack "S/(S/A* S/A*)", $blob;
Το πρότυπο της pack λέει: «ένας μετρητής 16-bit (ζευγών), μετά επανέλαβε το υπο-πρότυπο S/A* S/A* για κάθε ζεύγος.» Το πρότυπο της unpack διαβάζει τον μετρητή και τον εφαρμόζει απευθείας στην ομάδα - ο μετρητής δεν εμφανίζεται πλέον στη λίστα εξόδου.
Οριακές περιπτώσεις και περιορισμοί#
Το
/δεν έχει νόημα με στοιχείο σταθερού μήκους. Η δεύτερη οδηγία πρέπει να είναι μεταβλητού πλάτους:a*,A*,Z*,/A$n, ή κάτι ανάλογο. Η Perl θα απορρίψει δεύτερο στοιχείο σταθερού μήκους.Το
()*με τηνpackδεν μπορεί να αντιστοιχιστεί από()*στην unpack. Η pack έχει τις τιμές, οπότε μπορεί να πει «επανέλαβε μέχρι να τελειώσει». Η unpack δεν γνωρίζει πόσες επαναλήψεις είναι κωδικοποιημένες στον ενταμιευτή εκτός αν μια οδηγία μετρητή προηγείται της ομάδας.Οι εμφωλευμένες ομάδες είναι θεμιτές και συνηθισμένες:
pack "((CC)(S))<", @records
Οι τροποποιητές endianness διαχέονται σε κάθε επίπεδο εμφώλευσης.
Ο μετρητής επανάληψης σε μια ομάδα εφαρμόζεται σε ολόκληρη την ομάδα και παίρνει τόσες επαναλήψεις τιμών. Το
(CS)3καταναλώνει έξι τιμές της λίστας, όχι τρεις.
Επόμενο κεφάλαιο: οι οδηγίες που μετακινούνται μέσα σε ένα πρότυπο χωρίς να παράγουν τιμή - x, X, @, ..