你什么时候使用unpack('h *'...)或pack('h *'...)?

时间:2010-10-04 17:12:39

标签: perl hex pack unpack

在Perl中,packunpack有两个用于将字节转换为/从十六进制转换的模板:

  

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代替Hpack使用unpack吗?我正在寻找一个具体的例子;如果你知道一台机器组织了它的字节,它是什么,你可以链接到它上面的一些文件吗?

我可以想一下你可以使用 h的例子,例如当你不关心格式时序列化一些数据,只要你能读回来的话。 ,但H对此有用。我正在寻找h 更多H更有用的示例。

3 个答案:

答案 0 :(得分:9)

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

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

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

- 编辑

以下是具体的源代码:下载Perl 5.00402 for DOS HERE,解压缩,

在文件Opcode.pm和Opcode.pl中,您可以在此处看到unpack("h*",$_[0]);的使用:

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

我没有完全遵循代码,但我怀疑这是从MS-DOS系统调用中恢复信息......

在Perl 5.8-8的perlport中,您有针对目标的字节序的这些建议测试:

  

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

     

冲突的存储订单使数字完全混乱。如果一个       little-endian host(英特尔,VAX)存储0x12345678305419896       十进制),一个大端主机(摩托罗拉,Sparc,PA)将其读作       0x78563412(十进制2018915346)。 Alpha和MIPS可以是:       Digital / Compaq以little-endian模式使用/使用它们; SGI / Cray使用       他们处于大端模式。在网络(套接字)中避免此问题       连接使用packunpack格式nN       “网络”订单。这些都保证便携。

     

从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-strings只能移植到v21474836470x7FFFFFFF),就是这样       EBCDIC到底有多远,或者更准确地说是UTF-EBCDIC。

似乎unpack("h*",...)的使用频率高于pack("h*",...)。我确实注意到return qq'unpack("F", pack("h*", "$hex"))'; Deparse.pm使用了IO-Compress,而pack("*h",...)在Perl 5.12中使用了pack|unpack("h*"...)

如果您想要更多示例,请参阅Google Code Search list。您可以看到{{1}}非常罕见,主要与确定平台的结尾性有关...

答案 1 :(得分:3)

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

答案 2 :(得分:0)

两者之间的区别只与你是使用big-endian还是little-endian数据有关。有时您无法控制数据的来源或目标,因此要打包的Hh标记可以为您提供选项。 VN出于同样的原因。