Изучаем Perl

       

Упаковка и распаковка двоичных данных


Данные о паролях и группах удобно использовать в текстовом виде. Информацию в других системных базах данных более естественно представлять иначе. Например, IP-адрес интерфейса обрабатывается внутренними механизмами как четырехбайтовое число. Хотя его часто представляют в текстовом виде (как четыре небольших целых числа, разделенных точками), такое преобразование — пустая трата времени, если зти данные в промежутке между преобразованиями не выводятся на зкран пользователя.

По зтой причино написанные на Perl сетевые программы, ожидающие или возвращающие IP-адрес, используют четырехбайтовую строку, одному символу которой соответстввует один байт в памяти. Хотя конструирование и интерпретация такой байтовой строки — довольно простая задача, решае-мая с помощью функций chr и ord (здесь не представленных), в Perl используется более зффективное решение, которое в равной степени при-менимо и к более сложньш структурам.

Функция pack по принципу работы немного похожа на функцию sprintf. Она получает строку, задающую формат, и список значений и упаковывает значення в одну строку. Однако в pack строка, задающая формат, предназначена для создания двоичной структури данных. Напри-мер, вот как можно взять четыре небольших целых числа и упаковать их в виде последовательности байтов без знака в строке:

5buf = packf'CCCC", 140, 186, 65, 25);

Здесь строка формата pack — четыре буквы С. Каждая С соответствует отдельному значенню, взятому из приведенного следом списка (подобно тому, что делает спецификация % в функций sprintf). Формат С (согласно man-страницам Perl, краткому справочнику, книге Programming Perl,

HTML-файлам и даже видеоролику Perl: The Motion Picture) обозначает один байт, вычисляемый из символьного значення без знака (короткого целого). Отрока-результат в переменной $buf представляет собой четырехсимвольную строку, в которой каждый символ задан одним байтом. Зти байты имеют значення 140, 186, 65 и 25 соответственно.

Аналогичным образом формат 1 генерирует длинное значение со знаком. На многих машинах зто четырехбайтовое число, хотя зтот формат зависит от конкретной машины. На четырехбайтовой "длинной" машине оператор


$buf = packC'l",0х41424344) ;

генерирует четырехсимвольную строку, состоящую из символов abcd или dcba -- в зависимости от того, какой порядок хранения байтов используется на данной машине: "младший в младшем" или "старший в младшем" (либо что-то совершенно иное, если зта машина "не говорит" на ASCII). Зто обіясняется тем, что мы упаковываем одно значение в четыре символа (для представлення длинного целого отводится четыре байта), а зто одно значение как раз состоит из байтов, представляющих коды ASCII первых четырех букв алфавита. Аналогичньш образом,

$buf = pack("ll", 0х41424344, 0х45464748);

создает восьмибайтовую строку, состоящую из букв abcdefgh или dcbahgfe, опять-таки в зависимости от того, какой порядок хранения байтов используется в данной машине — "младший в младшем" или "старший в младшем".

Полный перечень различных форматов, используемых для упаковки, приведен в справочной документации (perlfunc(l)



или Programming Perl). Мы приведем некоторые из них как примеры, но все, конечно, давать не будем.

Допустим, вам дали восьмибайтовую строку abcdefgh и сказали, что она является представлением хранящихся в памяти (один символ — один байт) двух длинных (четырехбайтовых) значений со знаком. Как ее интерпретиро-вать? Нужно воспользоваться функцией, обратной функции pack,— функ-цией unpack. Она берет строку управления форматом (как правило, иден-тичную той, которую вы указывали в функции pack) и строку данных и возвращает список значений, которые хранятся в соответствующих ячейках памяти. Давайте, например, распакуем такую строку:

($vall,$val2) = unpack("ll","ABCDEFGH");

Зто даст нам в переменной $vall нечто вроде 0х41424344, а может быть, и 0х44434241 (в зависимости от порядка хранения байтов). По сути дела, по возвращаемым значенням мы можем определить, на какой машине работаем — с порядком "младший в младшем" или "старший в младшем".

Пробельные символы в строке, задающей формат, игнорируются и ис-пользуются лишь для удобочитаемости. Число в зтой строке, как правило, задает повторение предндущей спецификации соответствующее количество раз. Например, сссс можно записать как С4 или С2С2, смысл от зтого не изменится. (Однако в некоторых спецификациях число, указанное после символа, задающего формат, является частью спецификации, позтому их подобным образом записывать нельзя.)



После символа формата может стоять также звездочка, которая задает повторное применение данного формата до тех пор, пока не обработана остальная часть списка значений или пока не создана остальная часть строки, содержащей двоичное представление (в зависимости от того, что вьшолняется — упаковка или распаковка). Вот еще один способ упаковки четырех символов без знака в одну строку:

$bu£ ” pack("C*", 140, 186, 65, 25);

Здесь указанные четыре значення полностью обрабатываются одной спецификацией формата. Если бы вам требовались два коротких целых и "максимально возможное количество символов без знака", то можно было бы написать примерно так:

$buf = pack("s2 C*", 3141, 5926, 5, 3, 5, 8, 9, 7, 9, 3, 2);

Здесь мы получаем первые два значення как короткие (й генерируем, вероятно, четыре или восемь символов), а остальные девять — как символы без знака (й генерируем, почти наверняка, девять символов).

Функция unpack co звездочкой в качестве спецификации может форми-ровать список злементов, длина которых заранее не определена. Например, при распаковке с использованием формата с* создается один злемент списка (число) для каждого символа строки. Так, оператор

@values = unpack("С*", "hello, world!\n");

позволяет сформировать список из 14 злементов, по одному для каждого символа строки.


Содержание раздела