קיבוץ ומונים#

בסוף הפרק הזה תוכלו לחזור על תבנית הוראות לאורך רשימת ערכים, להשתמש בצורות * ו־[…] של מונה חזרות, ולכתוב רשומות עם קידומת אורך תיאוריות־עצמן באמצעות הוראת /.

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

לקבוצה אין עלות בתים משלה - היא מנגנון תחבירי. האורך הארוז הכולל הוא מה שההוראות בתוכה היו מייצרות ללא הסוגריים.

קיבוץ עם endianness#

השימוש המעשי הבודד ביותר של קבוצה - תת־מבנה שכל מספר שלם בו חולק סדר בתים אחד:

my $rec = pack "(l s s)<", $id, $x, $y;
# same as "l<s<s<"

ה־< מתפזר לכל הוראה מסודרת־בתים בתוכה, כולל קבוצות מקוננות. הוראות שאינן מקבלות מתאם סדר־בתים (כמו C, a, Z) אינן מושפעות בשקט.

מוני חזרות במלואם#

אחרי כל הוראה או קבוצה, אפשר לכתוב:

צורה

משמעות

N

החילו את ההוראה/קבוצה N פעמים

*

החילו כל עוד נשארים ערכים. עבור x, X, @: שווה־ערך ל־0. עבור u: 45.

[N]

זהה ל־N

[templ]

מונה חזרות הוא אורך הבתים הארוז של התבנית בסוגריים

צורת ה־[template] היא הכלי לביטוי ״כמה בתים שלוקח 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 בתים, ואז כך וכך בתים של payload״. הוראת / קושרת את השניים לצעד אחד.

ב־pack: length-item/sequence-item#

כתבו שתי הוראות מופרדות בקו נטוי. הראשונה אורזת את האורך; השנייה אורזת את ה־payload. 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 בתים כמחרוזת בסגנון 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* קורא מונה בתים ואז כך וכך בתים. כל מה שאחרי הקו הנטוי מכבד אותו, וה־C הגורר אוסף את הבית הבא כמצופה.

דוגמה מפותחת: זוגות מפתח־ערך#

פרוטוקול שומר מילון כ־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-ביט אחד (של זוגות), ואז חזרו על תת־התבנית S/A* S/A* עבור כל זוג.״ תבנית ה־unpack קוראת את המונה ומחילה אותו על הקבוצה ישירות - המונה כבר לא נמצא ברשימת הפלט.

מקרי קצה ואילוצים#

  • ל־/ אין משמעות עם פריט באורך קבוע. ההוראה השנייה חייבת להיות ברוחב משתנה: a*, A*, Z*, /A$n, או דומה. Perl ידחה פריט שני באורך קבוע.

  • ()* עם pack אינו יכול להיות מותאם על־ידי ()* ב־unpack. ל־pack יש את הערכים, אז הוא יכול לומר ״חזרו עד הסוף״. unpack אינו יודע כמה חזרות מקודדות במאגר אלא אם הוראת מונה מקדימה את הקבוצה.

  • קבוצות מקוננות הן לגיטימיות ונפוצות:

    pack "((CC)(S))<", @records
    

    מתאמי endianness מתפזרים דרך כל רמת קינון.

  • מונה חזרות על קבוצה חל על הקבוצה כולה ולוקח כך וכך חזרות של ערכים. (CS)3 צורך שישה ערכי רשימה, לא שלושה.

פרק הבא: ההוראות הזזות בתוך תבנית מבלי לייצר ערך - x, X, @, ..