问题 你何时会使用unpack('h *'...)或pack('h *'...)?


在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


9509
2017-10-04 17:12


起源



答案:


回想一下糟糕的'天' MS-DOS 通过在寄存器上设置高半字节和低半字节并执行Interupt xx来控制某些OS功能。例如,Int 21访问了许多文件功能。您可以将高半字节设置为驱动器号 - 谁将拥有超过15个驱动器?低半字节作为该驱动器上的请求功能等。

这里 是一些旧的CPAN代码,使用pack描述来设置寄存器以执行MS-DOS系统调用。

布莱什!我根本不会错过MS-DOS ...

- 编辑

以下是具体的源代码:下载Perl 5.00402 for 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,您有针对目标的endianess的这些建议测试:

不同的CPU存储不同的整数和浮点数       订单(称为 字节序)和宽度(32位和64位是       今天最常见的)。这会影响您尝试传输的程序       二进制格式的数字,从一个CPU架构到另一个,       通常通过网络连接“直播”,或通过存储       数字到二级存储,如磁盘文件或磁带。

存储订单冲突使得数字完全混乱。如果一个       little-endian主机(Intel,VAX)商店 0x12345678 (305419896 在       十进制),一个大端主机(摩托罗拉,Sparc,PA)将其读作        0x78563412 (2018915346 十进制)。 Alpha和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

如果您需要区分可以使用的endian体系结构       其中一个变量设置如下:

   $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*"...) 是非常罕见的,主要与确定平台的结束有关...


9
2017-10-04 18:07



该代码使用 pack,但它也没有使用 h 要么 H 只有它 s 和 c。 - cjm


答案:


回想一下糟糕的'天' MS-DOS 通过在寄存器上设置高半字节和低半字节并执行Interupt xx来控制某些OS功能。例如,Int 21访问了许多文件功能。您可以将高半字节设置为驱动器号 - 谁将拥有超过15个驱动器?低半字节作为该驱动器上的请求功能等。

这里 是一些旧的CPAN代码,使用pack描述来设置寄存器以执行MS-DOS系统调用。

布莱什!我根本不会错过MS-DOS ...

- 编辑

以下是具体的源代码:下载Perl 5.00402 for 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,您有针对目标的endianess的这些建议测试:

不同的CPU存储不同的整数和浮点数       订单(称为 字节序)和宽度(32位和64位是       今天最常见的)。这会影响您尝试传输的程序       二进制格式的数字,从一个CPU架构到另一个,       通常通过网络连接“直播”,或通过存储       数字到二级存储,如磁盘文件或磁带。

存储订单冲突使得数字完全混乱。如果一个       little-endian主机(Intel,VAX)商店 0x12345678 (305419896 在       十进制),一个大端主机(摩托罗拉,Sparc,PA)将其读作        0x78563412 (2018915346 十进制)。 Alpha和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

如果您需要区分可以使用的endian体系结构       其中一个变量设置如下:

   $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*"...) 是非常罕见的,主要与确定平台的结束有关...


9
2017-10-04 18:07



该代码使用 pack,但它也没有使用 h 要么 H 只有它 s 和 c。 - cjm


我认为这在将数据传输到具有不同字节序的机器或从具有不同字节序的数据中读取数据时非常有用。如果某个进程希望以通常在内存中表示数据的方式接收数据,那么您最好以这种方式发送数据。


3
2017-10-04 17:16



我不认为endianess会进入它,因为这是nybbles 中 一个字节。仍然以相同的顺序处理字节。 - cjm
混合或中间性也恰好存在。 - rafl
你能给出这样一台机器的具体例子吗? - cjm
根据维基百科,PDP-11就是一个例子。没有人会关心这些日子,真的,但仍然。显然还有一些方法可以在某些ARM机器上获得类似的数据。此外,您似乎假设您正在使用的数据始终是字节对齐的。 - rafl
@cjm:当人们说“endianness”时,他们通常会谈论字节顺序,但实际上主题更广泛,并且可以包括nybble甚至位排序。最终,内部表示依赖于硬件,可以是设计师提出的任何疯狂方案。 - Michael Carman


两者之间的区别只与您是使用big-endian还是little-endian数据有关。有时您无法控制数据的来源或目的地,所以 H 和 h 要包装的标志是给你的选择。 V 和 N 是出于同样的原因。


0
2017-10-04 17:17



我不认为endianess会进入它,因为这是nybbles 中一个字节。仍然以相同的顺序处理字节。 - cjm
正如所提到的 rafl,这些是在蓝色边缘边缘的案例,你必须处理“有趣的”数据,认为遗留系统和深奥的文档记录不完整的二进制文件格式 - Eric Strom