Це команда perlpacktut, яку можна запустити в постачальнику безкоштовного хостингу OnWorks за допомогою однієї з наших численних безкоштовних робочих станцій, таких як Ubuntu Online, Fedora Online, онлайн-емулятор Windows або онлайн-емулятор MAC OS
ПРОГРАМА:
ІМ'Я
perlpacktut - підручник по "упакувати" та "розпакувати"
ОПИС
"pack" і "unpack" - це дві функції для перетворення даних відповідно до визначених користувачем
шаблон між захищеним способом зберігання значень Perl і деяким добре визначеним уявленням
як це може знадобитися в середовищі програми Perl. На жаль, їх теж двоє
з найбільш неправильно зрозумілих і найчастіше ігнорованих функцій, які надає Perl. Це
підручник розкриє їх для вас.
Команда Базовий Принцип
Більшість мов програмування не захищають пам'ять, де зберігаються змінні. В C, для
Наприклад, ви можете взяти адресу деякої змінної, і оператор "sizeof" підкаже вам
скільки байтів виділено змінній. Використовуючи адресу та розмір, ви можете
отримати доступ до сховища до душі.
У Perl ви просто не можете отримати доступ до пам'яті випадковим чином, але до структурної та репрезентативної
Перетворення, надане "pack" і "unpack", є чудовою альтернативою. "пакет"
функція перетворює значення в послідовність байтів, що містить уявлення відповідно до a
задана специфікація, так званий аргумент "шаблон". "розпакувати" - це зворотний процес,
отримання деяких значень із вмісту рядка байтів. (Однак зверніть увагу, що
не все, що було запаковано разом, можна акуратно розпакувати - дуже поширений досвід, як
досвідчені мандрівники, ймовірно, підтвердять.)
Ви можете запитати, навіщо вам потрібен шматок пам’яті, що містить деякі значення в двійковому вигляді
представництво? Однією з вагомих причин є введення та вихід, доступ до якогось файлу, пристрою чи файлу
мережеве з'єднання, завдяки чому це двійкове представлення або нав'язано вам, або буде
дати вам певну перевагу в обробці. Інша причина - передача даних у якийсь системний виклик
яка недоступна як функція Perl: «системний виклик» вимагає від вас надати параметри
зберігається так, як це відбувається в програмі C. Рівномірна обробка тексту (як показано на наступному
розділ) можна спростити за розумного використання цих двох функцій.
Щоб побачити, як працює (роз)пакування, ми почнемо з простого коду шаблону, де відбувається перетворення
перебуває на низькій швидкості: між вмістом послідовності байтів і шістнадцятковим рядком
цифри. Давайте використаємо "розпакувати", оскільки це, ймовірно, нагадає вам про програму дампу або щось інше
відчайдушне останнє повідомлення нещасні програми зазвичай надсилають вам до закінчення терміну їх дії
в дику синь там. Якщо припустити, що змінна $mem містить послідовність байтів, що
ми хотіли б перевірити, нічого не припускаючи його значення, ми можемо написати
my( $hex ) = unpack( 'H*', $mem);
надрукувати "$hex\n";
після чого ми можемо побачити щось подібне, коли кожна пара шістнадцяткових цифр відповідає
байт:
41204d414e204120504c414e20412043414e414c2050414e414d41
Що було в цьому шматку пам’яті? Цифри, символи чи суміш того й іншого? Припускаючи це
ми знаходимося на комп’ютері, де використовується кодування ASCII (або якесь подібне): шістнадцяткові значення в
діапазон 0x40 - 0x5A вказує на велику літеру, а 0x20 кодує пробіл. Тож можемо
припустимо, що це фрагмент тексту, який деякі можуть читати, як таблоїд; але інші будуть
потрібно взяти в руки таблицю ASCII і знову пережити це відчуття першокласника. Теж байдуже
багато про те, як це читати, ми зазначимо, що "розпакувати" з кодом шаблону "H"
перетворює вміст послідовності байтів у звичайний шістнадцятковий запис.
Оскільки «послідовність» є досить нечітким показником кількості, «H» було визначено як
перетворити лише одну шістнадцяткову цифру, якщо за нею слідує повторний підрахунок. An
зірочка для підрахунку повторів означає використовувати все, що залишилося.
Обернена операція - пакування вмісту байтів із рядка шістнадцяткових цифр - є
так само легко написано. Наприклад:
my $s = pack( 'H2' x 10, 30..39 );
надрукувати "$s\n";
Оскільки ми надаємо список із десяти 2-значних шістнадцяткових рядків для «упакування», шаблон пакування
має містити десять кодів упаковки. Якщо це запущено на комп’ютері з кодуванням символів ASCII,
він надрукує 0123456789.
Упаковка текст
Припустимо, вам потрібно прочитати такий файл даних:
Дата |Опис | Доходи|Витрати
01 Zed's Camel Emporium 24
01 Спрей від бліх 28
01 Катання на верблюдах туристам 29
Як ми це робимо? Ви можете спочатку подумати про використання "split"; однак, оскільки «розкол» руйнується
пусті поля, ви ніколи не дізнаєтеся, чи був запис – доходом чи видатком. Ой. Добре,
ви завжди можете використовувати "substr":
поки (<>) {
моя $date = substr($_, 0, 11);
мій $desc = substr($_, 12, 27);
мій $дохід = substr($_, 40, 7);
мій $expend = substr($_, 52, 7);
...
}
Насправді це не бочка сміху, чи не так? Насправді це гірше, ніж може здатися; в
Орлиний погляд може помітити, що перше поле має бути лише 10 символів завширшки, і
помилка поширилася на інші числа, які нам довелося рахувати вручну.
Тому він схильний до помилок, а також жахливо недружній.
Або, можливо, ми могли б використовувати регулярні вирази:
поки (<>) {
my($date, $desc, $income, $expend) =
m|(\d\d/\d\d/\d{4}) (.{27}) ({7})(.*)|;
...
}
Ура Ну, це трохи краще, але - добре, ви б хотіли зберегти це?
Гей, хіба Perl не повинен полегшувати подібні речі? Ну, це так, якщо ви використовуєте
правильні інструменти. "упакувати" та "розпакувати" призначені, щоб допомогти вам у роботі з фіксованими
дані про ширину, як наведені вище. Давайте подивимося на рішення з "розпакувати":
поки (<>) {
my($date, $desc, $income, $expend) = unpack("A10xA27xA7A*", $_);
...
}
Це виглядає трохи приємніше; але ми повинні розібрати цей дивний шаблон. Куди я потягнув
що з?
Добре, давайте ще раз подивимося на деякі наші дані; фактично, ми включимо заголовки та a
зручна лінійка, щоб ми могли відстежувати, де ми знаходимося.
1 2 3 4 5
1234567890123456789012345678901234567890123456789012345678
Дата |Опис | Доходи|Витрати
01 Спрей від бліх 28
01 Катання на верблюдах туристам 29
З цього ми бачимо, що стовпець дати тягнеться від колонки 1 до стовпця 10 - десять
символи широкі. "pack"-ese для "персонажу" - це "A", а десять з них - "A10". Тож якщо
ми просто хотіли витягти дати, ми могли б сказати так:
my($date) = unpack("A10", $_);
Добре, що далі? Між датою та описом є порожній стовпець; ми хочемо пропустити
над цим. Шаблон "x" означає "пропустити вперед", тому ми хочемо одного з них. Далі маємо
ще одна партія символів, від 12 до 38. Це ще 27 символів, отже, «A27». (Не роби
зробіть помилку в стовпчику огорожі - між 27 і 12 є 38 символів, а не 26. Порахуйте їх!)
Тепер ми пропускаємо ще одного персонажа і вибираємо наступні 7 символів:
my($date,$description,$income) = unpack("A10xA27xA7", $_);
Тепер настає хитра частина. Рядки в нашій книзі, які є лише доходами, а не витратами
може закінчуватися на стовпці 46. Отже, ми не хочемо говорити нашому шаблону «розпакувати», що ми необхідність до
знайти ще 12 символів; ми просто скажемо «якщо щось залишилося, візьми». Як ти
можна здогадатися з регулярних виразів, ось що означає «*»: «використовувати все
залишилося".
· Майте на увазі, що на відміну від регулярних виразів, шаблон «розпакувати» цього не робить
відповідати вхідним даним, Perl закричить і помре.
Отже, зібравши все разом:
my ($date, $description, $income, $expend) =
unpack("A10xA27xA7xA*", $_);
Тепер наші дані проаналізовано. Я вважаю, що ми могли б захотіти зробити зараз, так це збільшити наш дохід
і витрати, і додайте ще один рядок у кінець нашої книги - у тому самому форматі -
сказати, скільки ми принесли і скільки витратили:
поки (<>) {
my ($date, $desc, $income, $expend) =
unpack("A10xA27xA7xA*", $_);
$tot_income += $дохід;
$tot_expend += $expend;
}
$tot_income = sprintf("%.2f", $tot_income); # Введіть їх
$tot_expend = sprintf("%.2f", $tot_expend); # «фінансовий» формат
$date = POSIX::strftime("%m/%d/%Y", локальний час);
# ОК, давайте йти:
print pack("A10xA27xA7xA*", $date, "Totals",
$tot_income, $tot_expend);
О, хм. Це не зовсім спрацювало. Давайте подивимося, що сталося:
01 Zed's Camel Emporium 24
01 Спрей від бліх 28
01 Катання на верблюдах туристам 29
03 р. Всього 23
Добре, це початок, але що сталося з пробілами? Ми поставили «х», чи не так? Чи не так
пропустити вперед? Давайте подивимося, що говорить «pack» у perlfunc:
x Нульовий байт.
Ура Не дивно. Існує велика різниця між «нульовим байтом», нульовим символом та «a
пробіл", символ 32. Perl поставив щось між датою та описом - але
на жаль, ми цього не бачимо!
Насправді нам потрібно розширити ширину полів. Формат "А" заповнює будь-які
неіснуючі символи з пробілами, тому ми можемо використовувати додаткові пробіли для вибудовування наших
поля, наприклад:
print pack("A11 A28 A8 A*", $date, "Totals",
$tot_income, $tot_expend);
(Зверніть увагу, що ви можете поставити пробіли в шаблон, щоб зробити його більш читабельним, але вони цього не роблять
перевести на пробіли у вихідних даних.) Ось що ми отримали цього разу:
01 Zed's Camel Emporium 24
01 Спрей від бліх 28
01 Катання на верблюдах туристам 29
03 р. Всього 23 2001
Це трохи краще, але у нас все ще є останній стовпець, який потрібно перемістити далі
закінчено. Є простий спосіб виправити це: на жаль, ми не можемо змусити «упакувати» правильно-
обґрунтуйте наші поля, але ми можемо отримати "sprintf" для цього:
$tot_income = sprintf("%.2f", $tot_income);
$tot_expend = sprintf("%12.2f", $tot_expend);
$date = POSIX::strftime("%m/%d/%Y", локальний час);
print pack("A11 A28 A8 A*", $date, "Totals",
$tot_income, $tot_expend);
Цього разу ми отримуємо правильну відповідь:
01 Спрей від бліх 28
01 Катання на верблюдах туристам 29
03 р. Всього 23 2001
Таким чином ми споживаємо та виробляємо дані фіксованої ширини. Давайте підсумуємо те, що ми бачили
"упакувати" та "розпакувати" поки що:
· Використовуйте «пакет», щоб перейти від кількох частин даних до однієї версії фіксованої ширини; використовувати "розпакувати"
щоб перетворити рядок фіксованої ширини в кілька частин даних.
· Формат пакету «А» означає «будь-який символ»; якщо ви "пакуєте" і ви закінчили
речі, які потрібно упакувати, "pack" заповнить решту місцями.
· «x» означає «пропустити байт» під час «розпакування»; коли "упаковується", це означає "ввести нуль
байт» - мабуть, це не те, що ви маєте на увазі, якщо маєте справу з простим текстом.
· Ви можете слідувати форматам з числами, щоб вказати, скільки символів має бути вплинуто
у цьому форматі: «A12» означає «взяти 12 символів»; «x6» означає «пропустити 6 байтів» або
«символ 0, 6 разів».
· Замість цифри можна використовувати «*», щоб означати «споживати все, що залишилося».
попередження: при упаковці кількох фрагментів даних «*» означає лише «споживати все
поточний фрагмент даних". Тобто
pack("A*A*", $один, $два)
упаковує весь $один у перший "A*", а потім усі $XNUMX у другий. Це
Загальний принцип: кожен символ формату відповідає одному фрагменту даних
«пакет» вид.
Упаковка Номери
Так багато про текстові дані. Давайте перейдемо до м’ясних речей, які найкраще «пакувати» та «розпаковувати».
at: обробка двійкових форматів для чисел. Звичайно, існує не тільки один двійковий формат
- життя було б занадто простим, але Perl виконає всю вибагливу роботу за вас.
Цілі числа
Номери упаковки та розпакування мають на увазі перетворення на деякі та з них конкретний двійковий
представництво. Залишаючи на даний момент числа з плаваючою комою в сторону, головне
Властивості будь-якого такого представлення:
· кількість байтів, що використовуються для зберігання цілого числа,
· чи інтерпретується вміст як число з підписом чи без підпису,
· порядок байтів: чи є перший байт найменшим чи найбільш значущим байтом (або:
little-endian або big-endian, відповідно).
Так, наприклад, щоб упакувати 20302 у 16-розрядне ціле число зі знаком у вашому комп’ютері
уявлення, яке ви пишете
мій $ps = pack( 's', 20302 );
Знову, результатом є рядок, який тепер містить 2 байти. Якщо ви надрукуєте цей рядок (який є,
загалом, не рекомендується) ви можете побачити "ON" або "NO" (залежно від байта вашої системи
порядок) - або щось зовсім інше, якщо ваш комп'ютер не використовує символ ASCII
кодування. Розпакування $ps з тим самим шаблоном повертає вихідне ціле значення:
my( $s ) = unpack( 's', $ps );
Це справедливо для всіх числових шаблонних кодів. Але не чекайте чудес: якщо запаковані
значення перевищує відведену ємність байтів, біти вищого порядку безшумно відкидаються, і
unpack, звичайно, не зможе витягти їх назад з якогось чарівного капелюха. І коли пакуєш
використовуючи підписаний код шаблону, наприклад "s", надлишкове значення може призвести до знакового біта
налаштування та розпакування поверне від’ємне значення.
16 біт не заведуть вас занадто далеко з цілими числами, але є "l" і "L" для знаків і
беззнакові 32-розрядні цілі числа. А якщо цього недостатньо і ваша система підтримує 64 біт
цілі числа, ви можете розсунути межі набагато ближче до нескінченності за допомогою пакетних кодів "q" і "Q". А
помітний виняток надають коди пакету "i" і "I" для цілих чисел зі знаком і без знака
різновиду "локальний звичай": таке ціле число займе стільки ж байтів, скільки локальний C
компілятор повертає значення "sizeof(int)", але він буде використовувати at найменш 32 біт.
Кожен із цілих кодів пакету "sSlLqQ" призводить до фіксованої кількості байтів, незалежно від того
де ви виконуєте свою програму. Це може бути корисно для деяких програм, але це не так
забезпечують переносний спосіб передачі структур даних між програмами на Perl і C (прив’язані до
відбувається, коли ви викликаєте розширення XS або функцію Perl "syscall"), або коли ви читаєте або
записувати двійкові файли. У цьому випадку вам знадобляться шаблонні коди, які залежать від чого
ваш локальний компілятор C компілює, наприклад, коли ви кодуєте "short" або "unsigned long".
Ці коди та відповідні їм байти наведені в таблиці нижче. Оскільки
Стандарт C залишає багато свободи щодо відносних розмірів цих типів даних,
фактичні значення можуть відрізнятися, і тому значення надаються у вигляді виразів у C і Perl.
(Якщо ви хочете використовувати значення з %Config у своїй програмі, вам потрібно імпортувати їх за допомогою «use
Конфігурація".)
Довжина байта без знака в C довжина байта в Perl
s! S! sizeof(short) $Config{shortsize}
я! я! sizeof(int) $Config{intsize}
я! Л! sizeof(long) $Config{longsize}
q! Q! sizeof(long long) $Config{longlongsize}
"Я!" і я!" коди не відрізняються від «i» та «I»; їх терплять
задля повноти.
Розпакування a Стек Кадр
Під час роботи з двійковими даними може знадобитися запит на певний порядок байтів
походить від певної архітектури, тоді як ваша програма може працювати на повністю
різна система. Як приклад, припустимо, що у вас є 24 байти, що містять кадр стека
відбувається на Intel 8086:
+--------+ +----+----+ +---------+
TOS: | IP | TOS+4:| FL | FH | ПРАПОРИ TOS+14:| SI |
+--------+ +----+----+ +---------+
| CS | | AL | AH | СОКИРА | DI |
+--------+ +----+----+ +---------+
| BL | BH | BX | АТ |
+----+----+ +---------+
| CL | CH | CX | DS |
+----+----+ +---------+
| DL | DH | DX | ES |
+----+----+ +---------+
По-перше, ми зазначаємо, що цей відомий часом 16-розрядний процесор використовує порядок малого кінця, і ось чому
байт молодшого порядку зберігається за нижчою адресою. Розпакувати такий (непідписаний) короткий ми будемо
потрібно використовувати код "v". Підрахунок повторів розпаковує всі 12 шортів:
my( $ip, $cs, $flags, $ax, $bx, $cd, $dx, $si, $di, $bp, $ds, $es ) =
unpack('v12', $frame);
Як альтернатива, ми могли б використати "C", щоб розпакувати індивідуально доступні регістри байтів
FL, FH, AL, AH тощо:
my( $fl, $fh, $al, $ah, $bl, $bh, $cl, $ch, $dl, $dh ) =
unpack( 'C10', substr( $frame, 4, 10 ) );
Було б непогано, якби ми могли зробити це одним махом: розпакувати коротку упаковку, трохи зробити резервну копію,
а потім розпакуйте 2 байти. Оскільки Perl is добре, він пропонує код шаблону "X" для резервного копіювання
один байт. Об’єднавши все це разом, ми можемо написати:
мій( $ip, $cs,
$flags,$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 );
(Незграбної конструкції шаблону можна уникнути – просто читайте далі!)
Ми доклали зусиль, щоб створити шаблон, щоб він відповідав вмісту нашого
буфер кадру. Інакше ми або отримаємо невизначені значення, або «розпакувати» не вдалося б розпакувати
всі. Якщо в "pack" закінчиться елементи, він надасть нульові рядки (які примусово вводяться
нулі, коли це говориться в коді пакета).
Як до Є an Яйце on a нетто
Пакувальний код для big-endian (байт старшого порядку за найнижчою адресою) - "n" для 16 біт і
"N" для 32-розрядних цілих чисел. Ви використовуєте ці коди, якщо знаєте, що ваші дані надходять від a
сумісна архітектура, але, як не дивно, ви також повинні використовувати ці коди пакетів if
ви обмінюєтеся двійковими даними через мережу з якоюсь системою, яку ви знаєте поруч
нічого про. Проста причина полягає в тому, що цей порядок було обрано як мережу порядок,
і всі програми, що побоюються стандартів, повинні дотримуватися цієї конвенції. (Це, звичайно, а
сувора підтримка однієї з ліліпутських партій і цілком може вплинути на політичну
розробка там.) Отже, якщо протокол очікує, що ви відправите повідомлення, відправивши
спочатку довжина, а потім стільки байтів, ви можете написати:
мій $buf = pack( 'N', length( $msg ) ) . $msg;
або навіть:
my $buf = pack( 'NA*', length( $msg), $msg);
і передайте $buf у вашу процедуру надсилання. Деякі протоколи вимагають, щоб підрахунок включався
довжина самого підрахунку: потім просто додайте 4 до довжини даних. (Але обов’язково прочитайте
«Довжини та ширини», перш ніж ви дійсно кодуєте це!)
Порядок байтів модифікатори
У попередніх розділах ми дізналися, як використовувати «n», «N», «v» і «V» для упаковки та розпакування
цілі числа з великим або малим порядком байтів. Хоча це добре, це все-таки скоріше
обмежений, оскільки він не враховує всі види цілих чисел зі знаком, а також 64-розрядні цілі числа. Для
наприклад, якщо ви хочете розпакувати послідовність 16-розрядних цілих чисел зі знаком великого кінця в
незалежним від платформи способом, вам доведеться написати:
my @data = unpack 's*', pack 'S*', unpack 'n*', $buf;
Це негарно. Починаючи з Perl 5.9.2, є набагато приємніший спосіб висловити своє бажання a
певний порядок байтів: модифікатори ">" і "<". ">" - це модифікатор великого порядка, тоді як "<"
є модифікатором little-endian. Використовуючи їх, ми могли б переписати наведений вище код так:
my @data = unpack 's>*', $buf;
Як бачите, «великий кінець» стрілки торкається «s», що є гарним способом
пам'ятайте, що ">" - це модифікатор великого порядка. Те ж саме працює для "<", де
"маленький кінець" торкається коду.
Ви, ймовірно, знайдете ці модифікатори ще більш корисними, якщо вам доведеться мати справу з великими або
C-структури з малим байтом. Обов’язково прочитайте «Упаковка та розпакування C-структур» для отримання додаткової інформації
на тому.
Плаваючий точка Номери
Для пакування чисел з плаваючою комою у вас є вибір між кодами упаковки "f", "d",
«F» і «D». «f» і «d» упаковують (або розпаковують) з одинарною чи подвійною точністю
представлення, як це надається вашою системою. Якщо ваша система це підтримує, "D" може бути
використовується для упаковки та розпакування ("long double") значень, що може запропонувати навіть більшу роздільну здатність, ніж
«f» або «d». Примітка: Що там він має різний довго подвійний форматах.
"F" упаковує "NV", який є типом з плаваючою комою, який використовується Perl внутрішньо.
Не існує такого поняття, як мережеве представлення для реальних величин, тому, якщо ви хочете надіслати свої
реальні числа за межами комп’ютера, вам краще дотримуватися текстового представлення,
можливо з використанням шістнадцяткового формату з плаваючою системою (уникаючи втрати десяткового перетворення), якщо
ви абсолютно впевнені, що на іншому кінці лінії. Тим більше
авантюрний, ви також можете використовувати модифікатори порядку байтів із попереднього розділу
коди з плаваючою комою.
Екзотичний шаблони
Біт Рядки
Біти - це атоми у світі пам'яті. Можливо, доведеться використовувати доступ до окремих бітів
або в крайньому випадку, або тому, що це найзручніший спосіб обробки ваших даних. Біт
string (un)packing перетворює рядки, що містять серію з 0 і 1 символів і
послідовність байтів, кожен із яких містить групу з 8 біт. Це майже так само просто
звуки, за винятком того, що є два способи записати вміст байта як біт
рядок. Давайте подивимося на анотований байт:
7 6 5 4 3 2 1 0
+----------------+
| 1 0 0 0 1 1 0 0 |
+----------------+
MSB LSB
Знову їдять яйця: дехто думає, що це слід написати у вигляді бітового рядка
"10001100", тобто починаючи з найбільшого біта, інші наполягають на "00110001".
Ну, Perl не є упередженим, тому ми маємо два коди бітових рядків:
$byte = pack('B8', '10001100'); # почати з MSB
$byte = pack('b8', '00110001'); # почати з LSB
Неможливо запакувати або розпакувати бітові поля - лише цілі байти. «пакувати» завжди
починається з наступної межі байта і "округлює" до наступного кратного 8, додаючи нуль
біти за потребою. (Якщо вам потрібні бітові поля, у perlfunc є "vec". Або ви можете
реалізуйте обробку бітового поля на рівні рядка символів, використовуючи split, substr і
конкатенація на розпакованих бітових рядках.)
Щоб проілюструвати розпакування бітових рядків, ми розберемо простий регістр стану ("-"
означає "зарезервований" біт):
+----------------+----------------+
| SZ - A - P - C | - - - - ODIT |
+----------------+----------------+
MSB LSB MSB LSB
Перетворення цих двох байтів у рядок можна зробити за допомогою шаблону розпакування 'b16'. До
отримати окремі значення бітів з бітового рядка, який ми використовуємо "split" з "пустим"
шаблон роздільника, який розбивається на окремі символи. Розрядні значення з
«зарезервовані» позиції просто присвоюються «undef», зручному позначення «Я не знаю
байдуже, куди це йде».
($carry, undef, $parity, undef, $auxcarry, undef, $zero, $sign,
$trace, $interrupt, $direction, $overflow) =
split( //, unpack('b16', $status));
Ми могли б також використати шаблон розпакування 'b12', оскільки останні 4 біти можуть бути
все одно ігнорується.
Uu-кодування
Ще один дивак в алфавіті шаблону - це "u", який містить "uuencoded рядок".
("uu" є скороченням від Unix-to-Unix.) Швидше за все, це кодування вам ніколи не знадобиться
техніка, яка була винайдена для подолання недоліків старомодної трансмісії
носії, які не підтримують окрім простих даних ASCII. Основний рецепт простий:
Візьміть три байти, або 24 біти. Розділіть їх на 4 шість пакетів, додавши пробіл (0x20) до кожної.
Повторюйте, поки всі дані не змішаються. Складіть групи по 4 байти в рядки не довше ніж
60 і прикрасити їх попереду оригінальною кількістю байтів (збільшеною на 0x20) і "\n"
в кінці. - Шеф-кухар «пакети» приготує це для вас за хвилину, коли ви виберете пачку
код "u" в меню:
мій $uubuf = pack('u', $bindat);
Підрахунок повторів після "u" встановлює кількість байтів, які потрібно помістити в uu-кодований рядок, який
за замовчуванням максимальне значення становить 45, але можна встановити деяке (менше) ціле число, кратне
три. "unpack" просто ігнорує кількість повторів.
справи Суми
Ще більш дивний код шаблону - "%"номер>. По-перше, тому що він використовується як префікс до
інший код шаблону. По-друге, тому що його взагалі не можна використовувати в «пакеті», і по-третє,
у "розпакувати", не повертає дані, визначені кодом шаблону, якому передує. Натомість
це дасть вам ціле число номер бітів, які обчислюються із значення даних шляхом do
суми. Для числових кодів розпакування великих успіхів не досягнуто:
мій $buf = pack( 'iii', 100, 20, 3 );
print unpack( '%32i3', $buf ), "\n"; # друкує 123
Для рядкових значень "%" повертає суму байтових значень, що позбавляє вас від суми
цикл з "substr" і "ord":
print unpack( '%32A*', "\x01\x10" ), "\n"; # друкує 17
Хоча код "%" документується як повернення "контрольної суми": не довіряйте йому
такі цінності! Навіть при застосуванні до невеликої кількості байтів вони не гарантують a
помітна відстань Хеммінга.
У зв’язку з «b» або «B», «%» просто додає біти, і це можна використати для
ефективно рахувати встановлені біти:
my $bitcount = unpack('%32b*', $mask);
І біт парності можна визначити так:
my $evenparity = unpack('%1b*', $mask);
Unicode
Юнікод — це набір символів, який може представляти більшість символів у більшості світу
мовами, забезпечуючи місце для понад мільйона різних символів. Unicode 3.1 визначає
94,140 0 символів. Основні латинські символи призначаються цифрам від 127 до XNUMX.
Latin-1 Додаток із символами, які використовуються в кількох європейських мовах, знаходиться в
наступний діапазон, до 255. Після ще кількох латинських розширень ми знаходимо набори символів з
мовами, що використовують нелатинські алфавіти, що перемежовуються різноманітними наборами символів, наприклад
символи валют, Цапф Дінгбати або Брайля. (Можливо, ви захочете відвідати
<http://www.unicode.org/> щоб подивитись на деякі з них - мої особисті улюблені телугу
і каннада.)
Набір символів Unicode пов’язує символи з цілими числами. Кодування цих чисел у
однакова кількість байтів більш ніж вдвічі перевищить вимоги до зберігання написаних текстів
латинськими алфавітами. Кодування UTF-8 дозволяє уникнути цього, зберігаючи найпоширеніші (з a
західна точка зору) символи в одному байте, а рідкісніші кодуються в трьох
або більше байтів.
Perl використовує UTF-8 внутрішньо для більшості рядків Unicode.
Так яке відношення це має до «пакету»? Ну, якщо ви хочете скласти рядок Unicode
(який внутрішньо закодований як UTF-8), ви можете зробити це, використовуючи код шаблону "U". Як
Наприклад, давайте створимо символ валюти євро (кодовий номер 0x20AC):
$UTF8{Євро} = pack('U', 0x20AC);
# Еквівалент: $UTF8{Euro} = "\x{20ac}";
Перевірка $UTF8{Euro} показує, що він містить 3 байти: "\xe2\x82\xac". Однак це
містить лише 1 символ, число 0x20AC. Подорож в обидва боки можна завершити «розпакувати»:
$Unicode{Euro} = unpack('U', $UTF8{Euro});
Розпакування за допомогою коду шаблону "U" також працює для рядків байтів, закодованих UTF-8.
Зазвичай вам потрібно запакувати або розпакувати рядки UTF-8:
# упакуйте та розпакуйте єврейський алфавіт
мій $alefbet = pack( 'U*', 0x05d0..0x05ea );
my @hebrew = unpack('U*', $utf);
Зверніть увагу: у загальному випадку вам краще використовувати Encode::decode_utf8 для декодування
Закодований UTF-8 рядок байтів у рядок Perl Unicode і Encode::encode_utf8 для кодування
Perl Unicode рядок у UTF-8 байт. Ці функції забезпечують засоби обробки недійсних байтів
послідовності та, як правило, мають більш дружній інтерфейс.
Інший Портативний двійковий кодування
Пакетний код "w" був доданий для підтримки портативної схеми кодування двійкових даних, яка
виходить за рамки простих цілих чисел. (Деталі можна знайти наhttp://Casbah.org/>, Скарабей
проект.) BER (двійкове зашифроване представлення) стиснене ціле число без знака зберігає базу
128 цифр, перша старша цифра, з якомога меншою кількістю цифр. Біт вісім (
старший біт) встановлюється на кожен байт, крім останнього. Для кодування BER немає обмеження розміру, але
Perl не буде впадати в крайнощі.
мій $berbuf = pack( 'w*', 1, 128, 128+1, 128*128+127);
Шістнадцятковий дамп $berbuf з пробілами, вставленими в потрібних місцях, показує 01 8100 8101
81807F. Оскільки останній байт завжди менше 128, «розпакувати» знає, де зупинитися.
шаблон Групування
До Perl 5.8 повторення шаблонів доводилося робити шляхом «х»-множення
рядки шаблону. Тепер є кращий спосіб, оскільки ми можемо використовувати пакетні коди "(" і ")"
у поєднанні з повторним підрахунком. Шаблон "розпакувати" з прикладу Stack Frame може
просто написати так:
unpack( 'v2 (vXXCC)5 v5', $frame )
Розглянемо цю функцію трохи докладніше. Почнемо з еквіваленту
join( '', map( substr( $_, 0, 1 ), @str ) )
який повертає рядок, що складається з першого символу кожного рядка. Використовуючи pack, ми
вмію писати
pack( '(A)'.@str, @str )
або, оскільки кількість повторів «*» означає «повторювати так часто, як потрібно», просто
pack( '(A)*', @str )
(Зверніть увагу, що шаблон «A*» упакував би лише $str[0] у повну довжину.)
Щоб упакувати дати, що зберігаються як триплети (день, місяць, рік), у масив @dates у послідовність
байт, байт, коротке ціле число, яке ми можемо записати
$pd = pack( '(CCS)*', map( @$_, @dates));
Щоб поміняти місцями пари символів у рядку (з парною довжиною), можна використовувати кілька
техніки. По-перше, давайте використаємо "x" і "X", щоб пропустити вперед і назад:
$s = pack( '(A)*', unpack('(xAXXAx)*', $s));
Ми також можемо використовувати «@», щоб перейти до зміщення, причому 0 — це місце, де ми були, коли ми перебували
останній "(" зустрічався:
$s = pack( '(A)*', unpack( '(@1A @0A @2)*', $s));
Нарешті, існує також зовсім інший підхід: розпаковуються шорти з великим порядком і
упаковуючи їх у зворотному порядку байтів:
$s = pack( '(v)*', unpack('(n)*', $s);
Довжини та Ширини
рядок Довжини
У попередньому розділі ми бачили мережеве повідомлення, яке було створено за допомогою префікса
довжина двійкового повідомлення до фактичного повідомлення. Ви побачите, що упаковка довжина, а потім
дуже багато байтів даних є часто використовуваним рецептом, оскільки додавання нульового байта не працюватиме
якщо нульовий байт може бути частиною даних. Ось приклад, де використовуються обидві методи:
після двох закінчених нульовими рядками з адресами джерела та призначення відображатиметься коротке повідомлення (до
мобільний телефон) надсилається після байта довжини:
my $msg = pack( 'Z*Z*CA*', $src, $dst, length( $sm), $sm);
Розпакувати це повідомлення можна за допомогою того самого шаблону:
( $src, $dst, $len, $sm ) = unpack( 'Z*Z*CA*', $msg);
Незабаром ховається тонка пастка: додавання ще одного поля після короткого повідомлення
(у змінній $sm) все в порядку під час пакування, але це не можна розпакувати наївно:
# запакуйте повідомлення
my $msg = pack( 'Z*Z*CA*C', $src, $dst, length( $sm), $sm, $prio);
Помилка # розпакування - $prio залишається невизначеним!
( $src, $dst, $len, $sm, $prio ) = unpack( 'Z*Z*CA*C', $msg);
Код пакету "A*" поглинає всі залишилися байти, а $prio залишається невизначеним! Перед нами
нехай розчарування послабить моральний дух: Perl також має козир, щоб зробити цей трюк,
трохи далі в рукаві. Дивитися це:
# упакувати повідомлення: ASCIIZ, ASCIIZ, довжина/рядок, байт
my $msg = pack( 'Z* Z* C/A* C', $src, $dst, $sm, $prio);
# розпакувати
( $src, $dst, $sm, $prio ) = unpack( 'Z* Z* C/A* C', $msg);
Об’єднання двох кодів пакету з косою рискою ("/") пов’язує їх з одним значенням із
список аргументів. У "pack" довжина аргументу береться і пакується відповідно до
перший код, а сам аргумент додається після перетворення за допомогою коду шаблону
після косої риски. Це позбавляє нас від вставки виклику "довжина", але він є
«розпакувати», де ми дійсно оцінюємо: значення байта довжини позначає кінець рядка
взяти з буфера. Оскільки ця комбінація не має сенсу, крім тих випадків, коли
код другого пакета не «a*», «A*» або «Z*», Perl вам не дозволить.
Код пакета, що передує «/», може бути будь-яким, що підходить для представлення числа: All the
числові двійкові коди пакетів і навіть текстові коди, такі як "A4" або "Z*":
# упакувати/розпакувати рядок, якому передує його довжина в ASCII
my $buf = pack( 'A4/A*', "Humpty-Dumpty");
# розпакувати $buf: '13 Humpty-Dumpty'
мій $txt = unpack('A4/A*', $buf);
«/» не реалізовано в Perls до версії 5.6, тому, якщо ваш код потрібен для роботи на старих
Perls, які вам потрібно буде "unpack('Z* Z* C')", щоб отримати довжину, а потім використовувати його для створення нового
розпакувати рядок. Наприклад
# упакувати повідомлення: ASCIIZ, ASCIIZ, довжина, рядок, байт
# (сумісний з 5.005)
my $msg = pack( 'Z* Z* CA* C', $src, $dst, length $sm, $sm, $prio);
# розпакувати
( undef, undef, $len) = unpack( 'Z* Z* C', $msg);
($src, $dst, $sm, $prio) = розпакувати ( "Z* Z* x A$len C", $msg);
Але той другий «розпакування» мчить вперед. Він не використовує простий літерний рядок для
шаблон. Тож, можливо, нам варто представити...
Dynamic шаблони
Досі ми бачили літерали, які використовуються як шаблони. Якщо в списку предметів упаковки немає
фіксованої довжини, потрібен вираз для побудови шаблону (завжди, коли для деяких
причини, "()*" не можна використовувати). Ось приклад: щоб певним чином зберігати іменовані рядкові значення
яку можна зручно проаналізувати програмою C, ми створюємо послідовність імен і null
закінчені рядки ASCII, з "=" між іменем і значенням, за яким слідує символ
додатковий нульовий байт розмежування. Ось як:
мій $env = pack( '(A*A*Z*)' . keys( %Env ) . 'C',
map( { ( $_, '=', $Env{$_} ) } ключі ( %Env ) ), 0 );
Давайте один за одним розглянемо гвинтики цього млина байтів. Існує виклик "map", який створює
елементи, які ми маємо намір помістити в буфер $env: до кожного ключа (у $_) він додає "="
роздільник і значення введення хеша. Кожна трійка упакована з кодом шаблону
послідовність "A*A*Z*", яка повторюється відповідно до кількості клавіш. (Так, ось що
Функція "ключі" повертає в скалярному контексті.) Щоб отримати останній нульовий байт, ми додаємо 0 у
кінець списку "пакет", який має бути заповнений символом "C". (Уважні читачі, можливо, помітили
що ми могли б пропустити 0.)
Для зворотної операції нам доведеться визначити кількість елементів у буфері
перш ніж ми зможемо дозволити «розпакувати» його розірвати:
мій $n = $env =~ tr/\0// - 1;
my %env = map( split( /=/, $_ ), unpack( "(Z*)$n", $env ));
"tr" підраховує нульові байти. Виклик "unpack" повертає список пар "ім'я-значення".
з яких розбирається в блоці "карта".
Підрахунок Повторення
Замість того, щоб зберігати сторожа в кінці елемента даних (або списку елементів), ми могли б
перед даними вказується підрахунок. Знову ми пакуємо ключі та значення хешу, передуючи кожному
з беззнаковим підрахунком короткої довжини, і спочатку ми зберігаємо кількість пар:
my $env = pack( 'S(S/A* S/A*)*', скалярні ключі (%Env), %Env);
Це спрощує зворотну операцію, оскільки кількість повторів може бути розпакована
код:
my %env = unpack( 'S/(S/A* S/A*)', $env);
Зауважте, що це один із рідкісних випадків, коли ви не можете використовувати той самий шаблон для "pack"
і "розпакувати", оскільки "pack" не може визначити кількість повторів для групи "()".
Intel HEX
Intel HEX – це формат файлу для представлення двійкових даних, переважно для програмування різних
мікросхеми, як текстовий файл. (Побачитиhttp://en.wikipedia.org/wiki/.hex> для докладного
опис іhttp://en.wikipedia.org/wiki/SREC_(формат_файлу)> для Motorola
Формат S-запису, який можна розгадати за допомогою тієї ж техніки.) Кожен рядок починається з
двокрапка (':'), за яким слідує послідовність шістнадцяткових символів, що вказують байт
вважати n (8 біт), адреса (16 біт, великий байт), тип запису (8 біт), n байти даних і
контрольна сума (8 біт), обчислена як найменший байт із суми двох доповнень
попередні байти. Приклад: ":0300300002337A1E".
Першим кроком обробки такого рядка є перетворення в двійковий шістнадцятковий
даних, щоб отримати чотири поля, перевіряючи контрольну суму. Не дивно: ми будемо
почніть з простого виклику "pack", щоб перетворити все в двійковий:
мій $binrec = pack('H*', substr($hexrec, 1));
Отримана послідовність байтів найбільш зручна для перевірки контрольної суми. Не гальмуйте
запрограмувати за допомогою циклу for, додаючи значення "ord" байтів цього рядка - "розпакувати"
код "%" - це те, що потрібно використовувати для обчислення 8-бітової суми всіх байтів, яка повинна бути рівною
до нуля:
die unpack ("%8C*", $binrec) == 0;
Нарешті, давайте отримаємо ці чотири поля. На даний момент у вас не повинно виникнути жодних проблем з
перші три поля - але як ми можемо використовувати кількість байтів даних у першому полі як a
довжина поля даних? Тут на допомогу, як дозволяють, приходять коди «х» і «Х».
стрибаючи вперед-назад у шнурку, щоб розпакувати.
my( $addr, $type, $data ) = unpack( "xn C X4 C x3 /a", $bin );
Код "x" пропускає байт, оскільки нам ще не потрібен підрахунок. Код "n" піклується про
16-розрядна ціла адреса з великим порядком, і "C" розпаковує тип запису. Перебуваючи на зміщенні 4,
там, де починаються дані, нам потрібен підрахунок. «X4» повертає нас до початкової точки, яка є
байт зі зміщенням 0. Тепер ми підбираємо рахунок і змінюємо масштаб до зміщення 4, де ми зараз знаходимося
повністю обладнаний для вилучення точної кількості байтів даних, залишаючи контрольну суму в кінці
один байт.
Упаковка та Розпакування C Структури
У попередніх розділах ми бачили, як упакувати числа та рядки символів. Якби це було
не для кількох зачіпів ми могли б одразу завершити цей розділ стислим зауваженням
що структури C не містять нічого іншого, і тому ви вже знаєте все, що є
до нього. Вибачте, ні: читайте далі, будь ласка.
Якщо вам доводиться мати справу з великою кількістю структур C, і ви не хочете зламати весь свій шаблон
рядків вручну, ви, ймовірно, захочете подивитися на модуль CPAN
"Перетворити::Двійковий::C". Він не тільки може аналізувати ваше джерело C безпосередньо, але також має вбудовані
на підтримку всіх шансів і результатів, описаних далі в цьому розділі.
Команда Вирівнювання Pit
Враховуючи швидкість і вимоги до пам’яті, баланс був нахилений
на користь швидшого виконання. Це вплинуло на те, як компілятори C виділяють пам'ять
структури: на архітектурах, де 16-розрядний або 32-розрядний операнд можна переміщати швидше між
місця в пам’яті або в регістр ЦП або з регістра ЦП, якщо він вирівняний за парним або множинним
з чотирьох або навіть за адресами, кратними восьми, компілятор C надасть вам таку швидкість
отримати перевагу, заповнюючи додаткові байти в структури. Якщо ви не перетнете берегову лінію С
навряд чи завдасть вам жодного горя (хоча вам слід бути в курсі, коли ви проектуєте великі дані
структур, або ви хочете, щоб ваш код переносився між архітектурами (ви хочете, щоб,
чи не так?)).
Щоб побачити, як це впливає на «упакувати» та «розпакувати», ми порівняємо ці дві структури C:
typedef struct {
char c1;
короткий s;
char c2;
довгий l;
} gappy_t;
typedef struct {
довгий l;
короткий s;
char c1;
char c2;
} dense_t;
Зазвичай компілятор C виділяє 12 байт змінній "gappy_t", але вимагає лише 8 байтів.
байтів для "dense_t". Після подальшого дослідження ми можемо намалювати карти пам’яті, які показують
де приховано додаткові 4 байти:
0 +4 +8 +12
+--+--+--+--+--+--+--+--+--+--+--+--+
|c1|xx| s |c2|xx|xx|xx| л | xx = заповнити байт
+--+--+--+--+--+--+--+--+--+--+--+--+
gappy_t
0 +4 +8
+--+--+--+--+--+--+--+--+
| л | h |c1|c2|
+--+--+--+--+--+--+--+--+
dense_t
І ось тут впадає перша примха: шаблони «упакувати» та «розпакувати» потрібно набити
з кодами "x", щоб отримати ці додаткові байти заповнення.
Природне запитання: "Чому Perl не може компенсувати прогалини?" вимагає відповіді. один
вагома причина полягає в тому, що компілятори C можуть надавати розширення (не ANSI), що дозволяють усілякі види
фантастичного контролю над тим, як структури вирівнюються, навіть на рівні окремої людини
структурне поле. І, якби цього було замало, є підступна річ під назвою «союз»
де кількість байтів заповнення не може бути отримана з вирівнювання наступного елемента
поодинці
Гаразд, тож давайте затиснемо кулю. Ось один із способів правильного вирівнювання шляхом вставки
коди шаблонів "x", які не беруть відповідний елемент зі списку:
my $gappy = pack( 'cxs cxxx l!', $c1, $s, $c2, $l );
Зверніть увагу на "!" після "l": ми хочемо переконатися, що ми запакували довге ціле число під час його компіляції
нашим компілятором C. І навіть зараз він працюватиме лише для платформ, де компілятор
вирівнює речі, як зазначено вище. І хтось десь має платформу, де її немає.
[Ймовірно, Cray, де "short", "int" і "long" - це 8 байтів. :-)]
Підрахунок байтів і спостереження за вирівнюваннями в довгих структурах обов’язково буде важким. Не є
чи є спосіб створити шаблон за допомогою простої програми? Ось така програма на C
трюк:
#включати
#включати
typedef struct {
char fc1;
короткий fs;
char fc2;
довгий 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");
}
Вихідний рядок можна використовувати як шаблон у виклику "pack" або "unpack":
my $gappy = pack( '@0c @2s! @4c @8l!', $c1, $s, $c2, $l );
Ну, ще один шаблонний код - ніби у нас не було багато. Але «@» рятує наш день, увімкнувши
нам, щоб вказати зміщення від початку буфера пакування до наступного елемента: Це
просто значення макросу "offsetof" (визначене в " ") повертається, коли дано a
Тип "struct" і одне з його назв поля ("member-designator" на стандартному C).
Ні використання зміщень, ні додавання "x" для подолання пробілів не є задовільними. (Просто уяви
що станеться, якщо структура зміниться.) Нам дійсно потрібен спосіб сказати «пропустити як
багато байтів, як потрібно для наступного кратного N". У вільній мові Templatese ви говорите це
з "x!N", де N замінено відповідним значенням. Ось наступна наша версія
структура упаковки:
my $gappy = pack( 'cx!2 scx!4 l!', $c1, $s, $c2, $l );
Це, безумовно, краще, але ми все одно повинні знати, як довго всі цілі числа, і
портативність далеко. Замість 2, наприклад, ми хочемо сказати «як би довго не було коротко
є". Але це можна зробити, вклавши відповідний код пакету в дужки: "[s]". Отже,
ось найкраще, що ми можемо зробити:
my $gappy = pack( 'cx![s] scx![l!] l!', $c1, $s, $c2, $l );
Справа з Endian-ність
Тепер уявіть, що ми хочемо запакувати дані для машини з іншим порядком байтів.
Спочатку нам потрібно з’ясувати, наскільки великі типи даних на цільовій машині насправді.
Припустимо, що довгі мають ширину 32 біти, а короткі — 16 біт. Тоді можна
перепишіть шаблон як:
my $gappy = pack( 'cx![s] scx![l] l', $c1, $s, $c2, $l );
Якщо цільова машина має дрібний кінець, ми можемо написати:
my $gappy = pack( 'cx![s] s< cx![l] l<', $c1, $s, $c2, $l );
Це змушує короткі та довгі члени бути з малим порядком, і це добре, якщо ви
не має забагато членів структури. Але ми також можемо використовувати модифікатор порядку байтів на a
згрупувати і написати наступне:
my $gappy = pack( '( cx![s] scx![l] l )<', $c1, $s, $c2, $l );
Це не так коротко, як раніше, але це робить більш очевидним те, що ми маємо намір зробити
Порядок байтів з малим порядком байтів для всієї групи, а не лише для окремих кодів шаблонів. Це може
також бути більш читабельними та легшими в обслуговуванні.
вирівнювання, Приймати 2
Боюся, що ми ще не закінчили з вирівнюванням. Гідра піднімається
ще одна потворна голова, коли ви пакуєте масиви структур:
typedef struct {
короткий відлік;
символ символу;
} клітинка_t;
typedef cell_t buffer_t[BUFLEN];
Де підказка? Заповнення не потрібно ні перед першим полем "count", ні між
це та наступне поля "glyph", то чому б нам просто не запакувати так:
# тут щось пішло не так:
pack( 's!a' x @buffer,
map{ ( $_->{count}, $_->{glyph} ) } @buffer);
Це пакує "3*@buffer" байти, але виявляється, що розмір "buffer_t" в чотири рази
«БУФЛЕН»! Мораль історії полягає в тому, що необхідне вирівнювання структури або масиву є
поширюється на наступний більш високий рівень, де ми повинні розглянути доповнення at кінець кожен з
компонент також. Таким чином, правильний шаблон:
pack( 's!ax' x @buffer,
map{ ( $_->{count}, $_->{glyph} ) } @buffer);
вирівнювання, Приймати 3
І навіть якщо взяти до уваги все вищесказане, ANSI все одно дозволяє це:
typedef struct {
char foo[2];
} foot_t;
відрізняються за розміром. Обмеження вирівнювання структури може бути більшим, ніж будь-яке з його
елементів. [І якщо ви думаєте, що це не впливає ні на що загальне, розчленуйте наступне
мобільний телефон, який ви бачите. Багато з них мають ядра ARM, а правила структури ARM роблять "sizeof
(foo_t)" == 4]
покажчики та цінності Як до Скористайтесь Їх
Назва цього розділу вказує на другу проблему, з якою ви можете зіткнутися рано чи пізно
коли ви пакуєте C-структури. Якщо функція, яку ви збираєтеся викликати, очікує, скажімо, "void *"
цінність, ти не може просто візьміть посилання на змінну Perl. (Хоча це значення
безперечно, це адреса пам'яті, а не адреса, де знаходиться вміст змінної
зберігається.)
Код шаблону "P" обіцяє упакувати "покажчик на рядок фіксованої довжини". Хіба це не що
ми хочемо? Давай спробуємо:
# виділяємо деяке сховище та запакуємо вказівник на нього
my $memory = "\x00" x $size;
мій $memptr = pack('P', $memory);
Але зачекайте: чи не повертає «пакет» просто послідовність байтів? Як ми можемо передати цей рядок
байтів до деякого коду C, що очікує покажчик, який, зрештою, є не що інше, як число? The
відповідь проста: ми повинні отримати числову адресу з байтів, повернутих "pack".
my $ptr = unpack('L!', $memptr);
Очевидно, це передбачає, що можна навести вказівник на беззнакове довге і
навпаки, що часто працює, але не слід сприймати як універсальний закон. - Тепер це
у нас є цей вказівник, наступне запитання: як ми можемо використати його з користю? Нам потрібен дзвінок
до деякої функції C, де очікується покажчик. The зчитування(2) на думку спадає системний виклик:
ssize_t read(int fd, void *buf, size_t count);
Прочитавши perlfunc, що пояснює, як використовувати «системний виклик», ми можемо написати цю функцію Perl
копіювання файлу на стандартний вихід:
вимагати 'syscall.ph'; # запустіть h2ph, щоб створити цей файл
підкіт ($){
мій $path = shift();
мій $size = -s $path;
my $memory = "\x00" x $size; # виділити трохи пам'яті
my $ptr = unpack('L', pack('P', $memory));
відкрити (F, $path) || die( "$шлях: неможливо відкрити ($!)\n" );
мій $fd = fileno(F);
my $res = syscall( &SYS_read, fileno(F), $ptr, $size);
надрукувати $memory;
закрити (F);
}
Це не зразок простоти і не взірець портативності, але він ілюструє
суть: ми можемо проникнути за лаштунки та отримати доступ до Perl, який інакше добре охороняється
пам'ять! (Важлива примітка: «системний виклик» Perl робить НЕ вимагає створення покажчиків
цей кільцевий шлях. Ви просто передаєте рядкову змінну, і Perl пересилає адресу.)
Як працює "розпакувати" з "P"? Уявіть якийсь вказівник у буфері, який збирається розпакувати:
Якщо це не нульовий покажчик (який розумно вироблятиме значення "undef"), ми маємо
початкова адреса - але що далі? Perl не може дізнатися, як довго ця «фіксована довжина
string" є, тож ви повинні вказати фактичний розмір як явну довжину після "P".
мій $mem = "abcdefghijklmn";
print unpack('P5', pack('P', $mem)); # друкує "abcde"
Як наслідок, "pack" ігнорує будь-яке число або "*" після "P".
Тепер, коли ми побачили «P» на роботі, ми могли б також закрутити «p». Навіщо нам потрібен а
другий код шаблону для упаковки покажчиків взагалі? Відповідь криється за простим фактом
що "розпакувати" з "p" обіцяє рядок, що закінчується нулем, починається з отриманої адреси
з буфера, і це означає довжину для елемента даних, який потрібно повернути:
my $buf = pack( 'p', "abc\x00efhijklmn");
надрукувати unpack( 'p', $buf ); # друкує "abc"
Хоча це може ввести в оману: як наслідок довжини, що мається на увазі
довжина рядка, число після пакетного коду "p" є числом повторів, а не довжиною, як після
"П".
Використання "pack(..., $x)" з "P" або "p", щоб отримати адресу, де насправді зберігається $x, необхідно
використовувати з обережністю. Внутрішні механізми Perl розглядають відношення між a
змінна і ця адреса є власною приватною справою, і нам байдуже, що ми
отримали копію. Тому:
· Не використовуйте "pack" з "p" або "P", щоб отримати адресу змінної, яка обов'язково буде відправлена
поза межами області дії (і тим самим звільняючи його пам’ять), перш ніж ви закінчите використовувати
пам'ять за цією адресою.
· Будьте дуже обережні з операціями Perl, які змінюють значення змінної. Додавання
щось у змінній, наприклад, може вимагати перерозподілу її пам'яті,
залишаючи вас з вказівником на нічийну землю.
· Не думайте, що ви можете отримати адресу змінної Perl, коли вона зберігається як файл
ціле чи подвійне число! "pack('P', $x)" змусить внутрішню змінну
подання до рядка, як якщо б ви написали щось на кшталт "$x .= ''".
Проте безпечно P- або p-пакувати рядковий литерал, оскільки Perl просто виділяє
анонімна змінна.
Pack Рецепти
Ось колекція (можливо) корисних консервованих рецептів «упакувати» та «розпакувати»:
# Перетворіть IP-адресу для функцій сокета
pack( "C4", split /\./, "123.4.5.6" );
# Підрахуйте біти в шматку пам'яті (наприклад, вектор вибору)
unpack( '%32b*', $mask );
# Визначте порядковий кінець вашої системи
$is_little_endian = unpack('c', pack('s', 1));
$is_big_endian = unpack('xc', pack('s', 1));
# Визначте кількість бітів у нативному цілі
$bits = unpack( '%32I!', ~0);
# Підготуйте аргумент для системного виклику nanosleep
my $timespec = pack('L!L!', $secs, $nanosecs);
Для простого дампу пам’яті ми розпаковуємо деякі байти в стільки ж пар шістнадцяткових цифр і
використовуйте "map" для обробки традиційного інтервалу - 16 байт до рядка:
мій $i;
надрукувати карту( ++$i % 16 ? "$_ " : "$_\n",
unpack( 'H2' x length( $mem ), $mem ) ),
довжина ( $mem ) % 16 ? "\n" : '';
Funnies розділ
# Витягування цифр з нізвідки...
роздрукувати unpack('C', pack('x')),
unpack( '%B*', pack('A') ),
unpack('H', pack('A')),
unpack( 'A', unpack('C', pack('A') ) ), "\n";
# Один на дорогу ;-)
my $advice = pack( 'все, що можна в фургоні');
Автори
Саймон Козенс і Вольфганг Лаун.
Використовуйте perlpacktut онлайн за допомогою служб onworks.net