Когда вы будете использовать unpack ('h *'...) или pack ('h *'...)?

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

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.

Ответ 1

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

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

Blech!!! Я вообще не пропустил MS-DOS...

- Изменить

Вот конкретный исходный код: Скачать 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-битная составляющая     наиболее распространенный сегодня). Это влияет на ваши программы, когда они пытаются передать     числа в двоичном формате от одной архитектуры процессора до другой,     обычно либо "живут" через сетевое соединение, либо путем хранения     номера для вторичного хранилища, такие как файл диска или лента.

Конфликтующие заказы на хранение делают полный беспорядок из числа. Если     little-endian host (Intel, VAX) хранит 0x12345678 (305419896 в     десятичный), хост-носитель большого размера (Motorola, Sparc, PA) читает его как     0x78563412 (2018915346 в десятичной системе). Альфа и MIPS могут быть:     Digital/Compaq использует/использует их в режиме little-endian; 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 (включено как     of 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*"...) встречается довольно редко и в основном связано с определением окончательности платформы...

Ответ 2

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

Ответ 3

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