Когда вы будете использовать распаковку ('h *' ...) или упаковку ('h *' ...)? - PullRequest
11 голосов
/ 04 октября 2010

В Perl pack и unpack имеют два шаблона для преобразования байтов в / из гекса:

h Шестнадцатеричная строка (сначала низкий nybble).
H Шестнадцатеричная строка (сначала высокий nybble).

Это лучше всего пояснить на примере:

use 5.010; # so I can use say
my $buf = "\x12\x34\x56\x78";

say unpack('H*', $buf); # prints 12345678
say unpack('h*', $buf); # prints 21436587

Как видите, H - это то, что люди обычно имеют в виду, когда думают о преобразовании байтов в / из шестнадцатеричного числа. Так в чем же цель h? Ларри, должно быть, подумал, что кто-то может его использовать, иначе он бы не стал его включать.

Можете ли вы привести пример из реальной жизни, где вы действительно хотите использовать h вместо H с pack или unpack? Я ищу конкретный пример ; если вы знаете о машине, которая организовала свои байты таким образом, что это было, и можете ли вы дать ссылку на какую-нибудь документацию по ней?

Я могу вспомнить примеры, где вы могли бы использовать h, например, сериализацию некоторых данных, когда вам действительно не важно, какой формат, если вы можете их прочитать, но H было бы так же полезно для этого. Я ищу пример, где h больше полезно, чем H.

Ответы [ 3 ]

9 голосов
/ 04 октября 2010

Вспомните в плохие времена MS-DOS , что некоторые функции ОС контролировались путем установки высокого и низкого клевов в регистре и выполнения Interupt xx.Например, Int 21 получил доступ ко многим файловым функциям.В качестве номера диска вы бы указали высокий клев - у кого будет более 15 дисков ??Низкий клев в качестве запрошенной функции на этом диске и т. Д.

Здесь - это некоторый старый код CPAN, который использует упаковку, как вы описали, для установки регистров для выполнения системного вызова MS-DOS.

Blech !!!Я совсем не скучаю по MS-DOS ...

- Edit

Вот конкретный исходный код: Загрузите Perl 5.00402 для DOS ЗДЕСЬ , распакуйте,

В файлах Opcode.pm и Opcode.pl вы видите использование unpack("h*",$_[0]); здесь:

sub opset_to_hex ($) {
    return "(invalid opset)" unless verify_opset($_[0]);
    unpack("h*",$_[0]);
}

Я не следовал коду полностью, но я подозреваю, что этоЧтобы восстановить информацию из системного вызова MS-DOS ...

В perlport для Perl 5.8-8, у вас есть следующие предлагаемые тесты на постоянство цели:

Различные процессоры хранят целые числа и числа с плавающей запятой в разных порядках (называемых endianness ) и ширинах (32-битные и 64-битные являются наиболее распространенными сегодня).Это влияет на ваши программы, когда они пытаются передать числа в двоичном формате с одной архитектуры ЦП на другую, обычно либо «живыми» через сетевое соединение, либо путем сохранения чисел во вторичном хранилище, таком как файл на диске или лента.

Конфликтующие заказы на хранение запутывают числа.Если хост с прямым порядком байтов (Intel, VAX) хранит 0x12345678 (305419896 в десятичном формате), хост с прямым порядком байтов (Motorola, Sparc, PA) читает его как 0x78563412 (2018915346 в десятичном виде).Alpha и MIPS могут быть: Digital / Compaq использовал / использует их в режиме с прямым порядком байтов;SGI / Cray использует их в режиме с прямым порядком байтов.Чтобы избежать этой проблемы в сетевых (сокетных) соединениях, используйте форматы pack и unpack n и N, «сетевые» порядки.Они гарантированно являются переносимыми.

Начиная с perl 5.8.5, вы также можете использовать модификаторы > и < для принудительного порядка байтов с большим или меньшим порядком байтов.Это полезно, если вы хотите хранить целые числа со знаком или 64-битные целые числа, например.

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

   print unpack("h*", pack("s2", 1, 2)), "\n";
   # '10002000' on e.g. Intel x86 or Alpha 21064 in little-endian mode
   # '00100020' on e.g. Motorola 68040

Если вам необходимо различать порядковые архитектуры, вы можете использовать любую из переменных, установленных следующим образом:

   $is_big_endian    = unpack("h*", pack("s", 1)) =~ /01/;
   $is_little_endian = unpack("h*", pack("s", 1)) =~ /^1/;

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

Обе эти проблемы можно обойти двумя способами.Либо передавайте и храните числа всегда в текстовом формате, а не в двоичном формате, либо рассмотрите возможность использования таких модулей, как Data::Dumper (включены в стандартный дистрибутив с Perl 5.005) и Storable (включены с perl 5.8).Хранение всех данных в виде текста значительно упрощает дело.

V-строки переносимы только до v2147483647 (0x7FFFFFFF), вот как далеко зайдет EBCDIC, или, точнее, UTF-EBCDIC.

Кажется, что unpack("h*",...) используется чаще, чем pack("h*",...).Я заметил, что return qq'unpack("F", pack("h*", "$hex"))'; используется в Deparse.pm, а IO-Compress использует pack("*h",...) в Perl 5.12

Если вам нужны дополнительные примеры, вот список поиска кода Google .Вы можете видеть, что pack|unpack("h*"...) встречается довольно редко и в основном используется для определения бесконечности платформы ...

3 голосов
/ 04 октября 2010

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

0 голосов
/ 04 октября 2010

Различие между ними связано с тем, работаете ли вы с данными с прямым или прямым порядком байтов. Иногда у вас нет контроля над источником или местом назначения ваших данных, поэтому флаги H и h для упаковки есть, чтобы дать вам возможность. V и N существуют по той же причине.

...