Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 [ 67 ] 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

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

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

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

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

$buf = раскССССС", 140, 186, 65, 25);

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

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

$buf = раск{"1",0x41424344);

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



как раз состоит из байтов, представляющих коды ASCII первых четырех букв алфавита. Аналогичным образом,

$buf = раскСИ", 0x41424344, 0x45464748);

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

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

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

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

Это даст нам в переменной $vall нечто вроде 0x41424344, а может быть, и 0x44 43 4241 (в зависимости от порядка хранения байтов). По сути дела, по возвращаемым значениям мы можем определить, на какой машине работаем - с порядком "младший в младшем" или "старший в младшем".

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

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

$buf = раск("С*", 140, 186, 65, 25);

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

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



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

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

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

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

Получение информации о сети

Perl поддерживает сетевое программирование средствами, которые хорошо знакомы тем, кто писал программы для сетевых приложений на С. По сути дела, большинство функций Perl, обеспечиваюших доступ к сети, имеют и те же имена, что их С-коллеги, и похожие параметры. В этой главе мы не можем привести полную информацию по сетевому программированию, поэтому просто рассмотрим фрагмент сетевого приложения.

Один из параметров, который вам приходится часто определять,- это IP-адрес, соответствующий сетевому имени (или наоборот). В С вы преобразуете сетевое имя в сетевой адрес с помощью программы gethostbyname(3). Затем, используя полученный адрес, вы устанавливаете связь между своей программой и другой программой, которая работает где-то в другом месте.

В Perl функция, преобразующая хост-имя в адрес, имеет то же имя, что и С-программа, и похожие параметры. Выглядит она так:

($пагае, $aliases, $addrtype, $length, @addrs) = gethostbynarae($name); # основная форма функции gethostbynarae

Параметр этой функции - имя хоста, например, slate.bedrock.com, а возвращаемое значение - список из четырех и более параметров (в зависимости от того, сколько адресов связано с данным именем). Если имя хоста недействительно, функция возвращает пустой список.

Когда gethostbyname вызывается в скалярном контексте, возвращается только первый адрес.

Если gethostbyname завершается успешно, то переменной $name в качестве значения присваивается каноническое имя, которое, если входное имя - псевдоним, отличается от входного имени. Значение переменной $aliases - ЭТО список разделенных пробелами имен, под которыми данный хост известен в сети. Переменная $addrtype содержит кодовое обозначение формата представления адреса. Для имени slate .bedrock. com мы можем предположить, что это значение указывает на IP-адрес, обычно представляемый как четыре числа из диапазона от 1 до 256, разделенных точками.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 [ 67 ] 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103