何时使用unpack('h*' ...)或pack('h*' ...)?

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

h    一个十六进制字符串(低位字节在前)。
H    一个十六进制字符串(高位字节在前)。

下面通过例子来更好地解释:

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 的目的是什么呢?Larry 必须认为有人会使用它,否则他就不会费心去包含它。

你能给出一个真实世界的例子,在这个例子中,你实际上需要使用 h 而不是 Hpackunpack 吗? 我正在寻找一个具体的例子;如果您知道有一台机器按照这样的方式组织其字节,那么它是什么,并且您可以链接到一些相关文档吗?

我可以想到一些例子,您可以在其中使用 h,例如在序列化某些数据时,当您不太关心格式是什么,只要您可以读取它即可,但对于这种情况,H 也同样有用。我正在寻找一个比 H 更加有用的 h 的例子。

3个回答

13

在糟糕的“旧日子”中,MS-DOS控制着某些操作系统功能,通过设置寄存器上的高位和低位并执行中断xx来完成。例如,Int 21访问了许多文件功能。您会将高位设置为驱动器号--谁会拥有超过15个驱动器??将低位设置为该驱动器上请求的功能,等等。

这里是一些旧的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中,您可以对目标的字节顺序进行以下建议测试:

Different CPUs store integers and floating point numbers in different orders (called endianness) and widths (32-bit and 64-bit being the most common today). This affects your programs when they attempt to transfer numbers in binary format from one CPU architecture to another, usually either “live” via network connection, or by storing the numbers to secondary storage such as a disk file or tape.

Conflicting storage orders make utter mess out of the numbers. If a little-endian host (Intel, VAX) stores 0x12345678 (305419896 in decimal), a big-endian host (Motorola, Sparc, PA) reads it as 0x78563412 (2018915346 in decimal). Alpha and MIPS can be either: Digital/Compaq used/uses them in little-endian mode; SGI/Cray uses them in big-endian mode. To avoid this problem in network (socket) connections use the pack and unpack formats n and N, the “network” orders. These are guaranteed to be portable.

As of perl 5.8.5, you can also use the > and < modifiers to force big- or little-endian byte-order. This is useful if you want to store signed integers or 64-bit integers, for example.

You can explore the endianness of your platform by unpacking a data structure packed in native format such as:

   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

If you need to distinguish between endian architectures you could use either of the variables set like so:

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

Differing widths can cause truncation even between platforms of equal endianness. The platform of shorter width loses the upper parts of the number. There is no good solution for this problem except to avoid transferring or storing raw binary numbers.

One can circumnavigate both these problems in two ways. Either transfer and store numbers always in text format, instead of raw binary, or else consider using modules like Data::Dumper (included in the standard distribution as of Perl 5.005) and Storable (included as of perl 5.8). Keeping all data as text significantly simplifies matters.

The v-strings are portable only up to v2147483647 (0x7FFFFFFF), that's how far EBCDIC, or more precisely UTF-EBCDIC will go.

看起来 unpack("h*",...)pack("h*",...) 更常用。我注意到在 Deparse.pm 中使用了 return qq'unpack("F", pack("h*", "$hex"))';,而在 Perl 5.12 中 IO-Compress 使用了 pack("*h",...)

如果您想要更多的例子,这里有一个 Google Code Search list。您可以看到 pack|unpack("h*"...) 相对较少,大多数与确定平台字节顺序有关...


那段代码使用了 pack,但它没有与之一起使用 hH,只有 sc - cjm

4

我认为在向具有不同字节序的计算机传输数据或从其读取数据时,这可能特别有用。如果某个进程希望接收以内存中通常表示的方式呈现的数据,则最好按照那种方式发送数据。


我认为字节序并不重要,因为这是在一个字节内的nybbles。字节仍然按照相同的顺序进行处理。 - cjm
4
同时存在混合端序和中间端序。 - rafl
你能给出一个具体的这样的机器的例子吗? - cjm
2
根据维基百科,PDP-11是一个例子。现在真的没有人关心,但仍然存在这样的数据在某些ARM机器上出现的方式。此外,您似乎假设正在处理的数据始终是按字节对齐的。 - rafl
3
当人们说“字节序”时,通常是指字节顺序,但实际上这个话题更广泛,甚至可以包括半字节和位的排序。最终的内部表示取决于硬件,可能是设计者想出的任何疯狂方案。 - Michael Carman

2

这两者之间的区别只与您处理的数据是大端还是小端有关。有时您无法控制数据的来源或目的地,因此pack函数中的 Hh 标志为您提供了选择。同样的,VN 也是出于同样的原因而存在。


1
我认为字节序并不重要,因为这是在一个字节内的nybbles。字节仍然按照相同的顺序进行处理。 - cjm
2
正如 rafl 所提到的,这些情况只会在处理“有趣”的数据时出现,比如遗留系统和晦涩难懂的二进制文件格式。 - Eric Strom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接