【问题标题】:When would you use unpack('h*' ...) or pack('h*' ...)?什么时候使用 unpack('h*' ...) 或 pack('h*' ...)?
【发布时间】:2011-04-20 22:14: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的例子,比如当你并不关心格式是什么时序列化一些数据,只要你能读回来,但是H 对此同样有用。我正在寻找hH 更有用的示例。

【问题讨论】:

    标签: perl hex pack unpack


    【解决方案1】:

    回想MS-DOS 的糟糕“旧时代”,某些操作系统功能是通过在寄存器上设置高半字节和低半字节并执行中断 xx 来控制的。例如,Int 21 访问了许多文件函数。您会将高半字节设置为驱动器编号——谁将拥有超过 15 个驱动器?低半字节作为该驱动器上请求的功能等。

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

    白痴!!!我一点也不怀念 MS-DOS...

    --编辑

    这里是具体的源码:Download 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 主机 (Intel, VAX) 存储 0x12345678 (305419896 in 十进制),大端主机(摩托罗拉,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
    

    如果您需要区分字节序架构,您可以使用 像这样设置的任何一个变量:

       $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-strings 只能移植到v2147483647 (0x7FFFFFFF),也就是说 EBCDIC,或者更准确地说是 UTF-EBCDIC 会走多远。

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

    如果您想要更多示例,这里是Google Code Search list。您可以看到pack|unpack("h*"...) 相当罕见,主要与确定平台字节序有关...

    【讨论】:

    • 该代码使用pack,但它不使用hH,仅使用sc
    【解决方案2】:

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

    【讨论】:

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

    两者之间的区别只与您使用的是大端还是小端数据有关。有时您无法控制数据的来源或目的地,因此要打包的 Hh 标志可以为您提供选择。 VN 存在的原因相同。

    【讨论】:

    • 我不认为字节序会涉及到它,因为这是一个字节的nybbles。字节仍然以相同的顺序处理。
    • 正如rafl 所提到的,这些都是千载难逢的边缘案例,您必须处理“有趣”的数据,考虑遗留系统和深奥的记录不良的二进制文件格式跨度>
    猜你喜欢
    • 2019-02-24
    • 1970-01-01
    • 2019-10-19
    • 2010-09-22
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 2021-11-18
    • 1970-01-01
    相关资源
    最近更新 更多