根据 TEMPLATE 中给定的规则,将 LIST 中的值转换为字符串。生成的字符串是转换后的值的串联。通常,每个转换后的值看起来像它的机器级表示。例如,在 32 位机器上,整数可能由 4 个字节的序列表示,在 Perl 中将以 4 个字符长的字符串形式呈现。
有关此函数的介绍,请参见 perlpacktut。
TEMPLATE 是一个字符序列,它指定值的顺序和类型,如下所示
a A string with arbitrary binary data, will be null padded.
A A text (ASCII) string, will be space padded.
Z A null-terminated (ASCIZ) string, will be null padded.
b A bit string (ascending bit order inside each byte,
like vec()).
B A bit string (descending bit order inside each byte).
h A hex string (low nybble first).
H A hex string (high nybble first).
c A signed char (8-bit) value.
C An unsigned char (octet) value.
W An unsigned char value (can be greater than 255).
s A signed short (16-bit) value.
S An unsigned short value.
l A signed long (32-bit) value.
L An unsigned long value.
q A signed quad (64-bit) value.
Q An unsigned quad value.
(Quads are available only if your system supports 64-bit
integer values _and_ if Perl has been compiled to support
those. Raises an exception otherwise.)
i A signed integer value.
I An unsigned integer value.
(This 'integer' is _at_least_ 32 bits wide. Its exact
size depends on what a local C compiler calls 'int'.)
n An unsigned short (16-bit) in "network" (big-endian) order.
N An unsigned long (32-bit) in "network" (big-endian) order.
v An unsigned short (16-bit) in "VAX" (little-endian) order.
V An unsigned long (32-bit) in "VAX" (little-endian) order.
j A Perl internal signed integer value (IV).
J A Perl internal unsigned integer value (UV).
f A single-precision float in native format.
d A double-precision float in native format.
F A Perl internal floating-point value (NV) in native format
D A float of long-double precision in native format.
(Long doubles are available only if your system supports
long double values. Raises an exception otherwise.
Note that there are different long double formats.)
p A pointer to a null-terminated string.
P A pointer to a structure (fixed-length string).
u A uuencoded string.
U A Unicode character number. Encodes to a character in char-
acter mode and UTF-8 (or UTF-EBCDIC in EBCDIC platforms) in
byte mode. Also on EBCDIC platforms, the character number will
be the native EBCDIC value for character numbers below 256.
This allows most programs using this feature to not have to
care which type of platform they are running on.
w A BER compressed integer (not an ASN.1 BER, see perlpacktut
for details). Its bytes represent an unsigned integer in
base 128, most significant digit first, with as few digits
as possible. Bit eight (the high bit) is set on each byte
except the last.
x A null byte (a.k.a ASCII NUL, "\000", chr(0))
X Back up a byte.
@ Null-fill or truncate to absolute position, counted from the
start of the innermost ()-group.
. Null-fill or truncate to absolute position specified by
the value.
( Start of a ()-group.
在 TEMPLATE 中,某些字母后面可以可选地跟一个或多个修饰符(第二列列出了可以使用修饰符的字母)
! sSlLiI Forces native (short, long, int) sizes instead
of fixed (16-/32-bit) sizes.
! xX Make x and X act as alignment commands.
! nNvV Treat integers as signed instead of unsigned.
! @. Specify position as byte offset in the internal
representation of the packed string. Efficient
but dangerous.
> sSiIlLqQ Force big-endian byte-order on the type.
jJfFdDpP (The "big end" touches the construct.)
< sSiIlLqQ Force little-endian byte-order on the type.
jJfFdDpP (The "little end" touches the construct.)
>
和 <
修饰符也可以用于 ()
组,以强制对该组中的所有组件(包括其所有子组)使用特定的字节顺序。
以下规则适用
每个字母后面可以可选地跟一个数字,表示重复次数。数字重复次数可以可选地用方括号括起来,例如 pack("C[80]", @arr)
。重复次数在使用除 a
、A
、Z
、b
、B
、h
、H
、@
、.
、x
、X
和 P
之外的所有格式类型时,会从 LIST 中获取相应数量的值。在这些类型中,重复次数的含义不同,将在下面描述。如果使用 *
代替数字作为重复次数,则表示使用剩余的所有项,但以下情况除外:
@
、x
和 X
,它们等效于 0
。
<.>,表示相对于字符串的开头。
u
,等效于 1(或 45,这里等效)。
可以使用方括号内的模板字母来替换数字重复计数,以使用方括号内模板的打包字节长度作为重复计数。
例如,模板 x[L]
跳过与打包长整数相同的字节数,模板 "$t X[$t] $t"
解包 $t(当变量扩展时)解包的两倍。如果方括号内的模板包含对齐命令(例如 x![d]
),则其打包长度将按模板开头具有最大可能对齐的方式计算。
当与 Z
一起使用时,*
作为重复计数保证会添加一个尾随空字节,因此结果字符串始终比项目本身的字节长度长一个字节。
当与 @
一起使用时,重复计数表示从最内层 ()
组的开头开始的偏移量。
当与 .
一起使用时,重复计数确定计算值偏移量的起始位置,如下所示
如果重复计数为 0
,则相对于当前位置。
如果重复计数为 *
,则偏移量相对于打包字符串的开头。
如果它是整数 n,则偏移量相对于第 n 个最内层 ( )
组的开头,或者如果 n 大于组级别,则相对于字符串的开头。
u
的重复计数被解释为每行输出编码的最大字节数,其中 0、1 和 2 被替换为 45。重复计数不应超过 65。
a
、A
和 Z
类型只吞噬一个值,但将其打包为长度为 count 的字符串,根据需要用空字符或空格填充。解包时,A
去除尾随空格和空字符,Z
去除第一个空字符之后的所有内容,a
返回没有任何去除的数据。
如果要打包的值太长,则结果会被截断。如果它太长并且提供了显式计数,则 Z
只打包 $count-1
个字节,然后是一个空字节。因此,Z
始终打包一个尾随空字符,除非计数为 0。
同样,b
和 B
格式打包一个长度为那么多位的字符串。每个这样的格式生成结果的 1 位。这些通常后面跟着一个重复计数,例如 B8
或 B64
。
每个结果位基于对应输入字符的最低有效位,即 ord($char)%2
。特别是,字符 "0"
和 "1"
生成位 0 和 1,字符 "\000"
和 "\001"
也是如此。
从输入字符串的开头开始,每 8 个字符的元组被转换为 1 个输出字符。使用格式 b
,8 个字符元组的第一个字符决定一个字符的最低有效位;使用格式 B
,它决定一个字符的最高有效位。
如果输入字符串的长度不能被 8 整除,则余数将被打包,就好像输入字符串在末尾用空字符填充一样。类似地,在解包期间,将忽略“额外”的位。
如果输入字符串比需要的长,则会忽略剩余的字符。
重复计数的 *
使用输入字段的所有字符。在解包时,位被转换为 0
和 1
的字符串。
h
和 H
格式打包一个字符串,该字符串包含许多字节(4 位组,可表示为十六进制数字,"0".."9"
"a".."f"
)。
对于每种这样的格式,pack
生成 4 位的结果。对于非字母字符,结果基于输入字符的 4 个最低有效位,即 ord($char)%16
。特别是,字符 "0"
和 "1"
生成字节 0 和 1,字节 "\000"
和 "\001"
也是如此。对于字符 "a".."f"
和 "A".."F"
,结果与通常的十六进制数字兼容,因此 "a"
和 "A"
都生成字节 0xA==10
。使用此格式时,仅使用这些特定的十六进制字符。
从模板到 pack
的开头开始,每对字符被转换为 1 个输出字符。使用格式 h
,对的第一个字符决定输出字符的最低有效字节;使用格式 H
,它决定输出字符的最高有效字节。
如果输入字符串的长度不是偶数,则它表现得好像在末尾用空字符填充一样。类似地,在解包期间,将忽略“额外”的字节。
如果输入字符串比需要的长,则会忽略额外的字符。
重复计数的 *
使用输入字段的所有字符。对于 unpack
,字节被转换为十六进制数字的字符串。
p
格式将指向空终止字符串的指针打包。您有责任确保该字符串不是临时值,因为该值可能在您使用打包结果之前被释放。P
格式将指向长度指示的结构的指针打包。如果 p
或 P
的对应值为 undef
,则会创建空指针;类似地,使用 unpack
时,空指针会解包为 undef
。
如果您的系统具有奇怪的指针大小(这意味着指针的大小既不是 int 那么大,也不是 long 那么大),则可能无法以大端或小端字节序打包或解包指针。尝试这样做会引发异常。
/
模板字符允许打包和解包项目序列,其中打包的结构包含打包的项目计数,后面是打包的项目本身。当您要解包的结构在其自身中以单独的字段形式对某些字段的大小或重复计数进行编码时,这很有用。
对于 pack
,您编写 length-item/
sequence-item,而 length-item 描述了长度值的打包方式。最有可能使用的是整数打包格式,例如 n
用于 Java 字符串,w
用于 ASN.1 或 SNMP,以及 N
用于 Sun XDR。
对于 pack
,sequence-item 可以具有重复计数,在这种情况下,该计数与可用项目数量的最小值将用作 length-item 的参数。如果它没有重复计数或使用“*”,则使用可用项目的数量。
对于 unpack
,使用到目前为止解包的整数参数的内部堆栈。您编写 /
sequence-item,重复计数通过从堆栈中弹出最后一个元素来获得。sequence-item 不得具有重复计数。
如果 sequence-item 指的是字符串类型("A"
、"a"
或 "Z"
),则 length-item 是字符串长度,而不是字符串数量。对于打包的显式重复计数,打包的字符串将调整为该长度。例如
This code: gives this result:
unpack("W/a", "\004Gurusamy") ("Guru")
unpack("a3/A A*", "007 Bond J ") (" Bond", "J")
unpack("a3 x2 /A A*", "007: Bond, J.") ("Bond, J", ".")
pack("n/a* w/a","hello,","world") "\000\006hello,\005world"
pack("a/W2", ord("a") .. ord("z")) "2ab"
长度项不会从 unpack
中显式返回。
仅当使用 A
、a
或 Z
时,向长度项格式字母提供计数才有用。使用 a
或 Z
的长度项进行打包可能会引入 "\000"
字符,Perl 不认为这些字符在数字字符串中是合法的。
整数类型 s
、S
、l
和 L
后面可以加 !
修饰符来指定本机短整型或长整型。如上例所示,裸 l
表示正好 32 位,尽管本地 long
在本地 C 编译器中可能更大。这主要是在 64 位平台上出现的问题。您可以通过以下方式查看使用 !
是否有任何区别
printf "format s is %d, s! is %d\n",
length pack("s"), length pack("s!");
printf "format l is %d, l! is %d\n",
length pack("l"), length pack("l!");
i!
和 I!
也允许使用,但只是为了完整性:它们与 i
和 I
相同。
在构建 Perl 的平台上,本机短整型、整型、长整型和长长整型的实际大小(以字节为单位)也可以从命令行获得
$ perl -V:{short,int,long{,long}}size
shortsize='2';
intsize='4';
longsize='4';
longlongsize='8';
或通过 Config
模块以编程方式获得
use Config;
print $Config{shortsize}, "\n";
print $Config{intsize}, "\n";
print $Config{longsize}, "\n";
print $Config{longlongsize}, "\n";
$Config{longlongsize}
在不支持长长整型的系统上未定义。
整数格式 s
、S
、i
、I
、l
、L
、j
和 J
本质上在处理器和操作系统之间不可移植,因为它们遵循本机字节序和大小端。
0x12 0x34 0x56 0x78 # big-endian
0x78 0x56 0x34 0x12 # little-endian
基本上,英特尔和 VAX CPU 是小端,而其他所有 CPU(包括摩托罗拉 m68k/88k、PPC、Sparc、HP PA、Power 和 Cray)都是大端。Alpha 和 MIPS 可以是其中任何一种:Digital/Compaq 使用(或者说曾经使用)它们的小端模式,而 SGI/Cray 使用它们的大端模式。
大端和小端这两个名称是对经典乔纳森·斯威夫特讽刺作品《格列佛游记》中以吃鸡蛋方式命名的利立浦特小人国和布勒夫斯库岛人的一个幽默参考。这个词语进入计算机术语是通过丹尼·科恩的论文“关于圣战和对和平的呼吁”而来的,USC/ISI IEN 137,1980 年 4 月 1 日。
有些系统可能具有更奇怪的字节序,例如
0x56 0x78 0x12 0x34
0x34 0x12 0x78 0x56
这些被称为中端、中间端、混合端或只是奇怪。
您可以使用以下咒语来确定系统的字节序
printf("%#02x ", $_) for unpack("W*", pack L=>0x12345678);
在构建 Perl 的平台上的字节序也可以通过 Config 获得
use Config;
print "$Config{byteorder}\n";
或从命令行获得
$ perl -V:byteorder
字节序 "1234"
和 "12345678"
是小端;"4321"
和 "87654321"
是大端。具有多架构二进制文件的系统将具有 "ffff"
,表示静态信息不起作用,必须使用运行时探测。
对于可移植打包的整数,请使用 n
、N
、v
和 V
格式,或者使用下面描述的 >
和 <
修饰符。另请参见 perlport。
浮点数也有字节序。通常(但并非总是)它与整数的字节序一致。尽管大多数平台现在使用 IEEE 754 二进制格式,但仍然存在差异,尤其是在涉及长双精度数时。您可以查看 Config
变量 doublekind
和 longdblkind
(以及 doublesize
、longdblsize
):与 byteorder
不同,“kind” 值是枚举类型。
从可移植性的角度来看,最好的选择可能是坚持使用 IEEE 754 64 位双精度数,并使用一致的字节序。另一种可能性是 printf
的 "%a"
格式。
从 Perl 5.10.0 开始,整数和浮点数格式,以及 p
和 P
格式以及 ()
组,都可以在后面加上 >
或 <
字节序修饰符,分别强制使用大端或小端字节序。这些修饰符在 n
、N
、v
和 V
不涵盖有符号整数、64 位整数或浮点数的情况下特别有用。
在使用字节序修饰符时,请牢记以下几点:
在不同平台之间交换有符号整数仅在所有平台都以相同格式存储它们时才有效。大多数平台以二进制补码形式存储有符号整数,因此通常这不是问题。
>
或 <
修饰符只能在大小端或小端机器上的浮点数格式上使用。否则,尝试使用它们会引发异常。
在浮点数上强制使用大端或小端字节序以进行数据交换,仅在所有平台都使用相同的二进制表示(例如 IEEE 浮点数)时才有效。即使所有平台都使用 IEEE,仍然可能存在细微差异。能够在浮点数上使用 >
或 <
可能有用,但如果您不确切知道自己在做什么,也可能很危险。这不是一种通用的可移植存储浮点数的方法。
在 ()
组上使用 >
或 <
时,这会影响组内所有接受字节序修饰符的类型,包括所有子组。对于所有其他类型,它会被静默忽略。您不允许在已经具有字节序修饰符后缀的组内覆盖字节序。
实数(浮点数和双精度数)仅以本机机器格式存在。由于浮点数格式的多样性和缺乏标准的“网络”表示,因此没有提供交换机制。这意味着在一台机器上写入的打包浮点数数据可能无法在另一台机器上读取,即使两台机器都使用 IEEE 浮点数运算(因为内存表示的字节序不是 IEEE 规范的一部分)。另请参阅 perlport。
如果您确切地知道自己在做什么,可以使用 >
或 <
修饰符强制浮点值采用大端或小端字节序。
由于 Perl 在内部使用双精度浮点数(或如果配置了,则使用长双精度浮点数)进行所有数值计算,因此从双精度浮点数转换为单精度浮点数,然后再转换为双精度浮点数会导致精度损失,因此 unpack("f", pack("f", $foo)
) 通常不会等于 $foo。
Pack 和 unpack 可以以两种模式运行:字符模式(C0
模式),其中打包的字符串按字符处理;以及 UTF-8 字节模式(U0
模式),其中打包的字符串以其 UTF-8 编码的 Unicode 形式逐字节处理。字符模式是默认模式,除非格式字符串以 U
开头。您始终可以在格式中间使用显式的 C0
或 U0
切换模式。此模式将一直有效,直到下一个模式更改,或直到它(直接)应用到的 ()
组的末尾。
使用 C0
获取 Unicode 字符,同时使用 U0
获取非 Unicode 字节并不一定很明显。可能只有第一个是您想要的。
$ perl -CS -E 'say "\x{3B1}\x{3C9}"' |
perl -CS -ne 'printf "%v04X\n", $_ for unpack("C0A*", $_)'
03B1.03C9
$ perl -CS -E 'say "\x{3B1}\x{3C9}"' |
perl -CS -ne 'printf "%v02X\n", $_ for unpack("U0A*", $_)'
CE.B1.CF.89
$ perl -CS -E 'say "\x{3B1}\x{3C9}"' |
perl -C0 -ne 'printf "%v02X\n", $_ for unpack("C0A*", $_)'
CE.B1.CF.89
$ perl -CS -E 'say "\x{3B1}\x{3C9}"' |
perl -C0 -ne 'printf "%v02X\n", $_ for unpack("U0A*", $_)'
C3.8E.C2.B1.C3.8F.C2.89
您必须自己通过插入例如足够的 "x"
来进行任何对齐或填充。pack
和 unpack
无法知道字符将要来自哪里或去往哪里,因此它们将输出和输入处理为扁平的字符序列。
()
组是一个用括号括起来的子模板。组可以接受重复计数,既可以作为后缀,也可以对于 unpack
,还可以通过 /
模板字符。在组的每次重复中,使用 @
进行定位从 0 重新开始。因此,以下结果
pack("@1A((@2A)@3A)", qw[X Y Z])
是字符串 "\0X\0\0YZ"
。
x
和 X
接受 !
修饰符作为对齐命令:它们向前或向后跳到与 count
个字符的倍数对齐的最接近的位置。例如,要 pack
或 unpack
像
struct {
char c; /* one signed, 8-bit character */
double d;
char cc[2];
}
这样的 C 结构,可能需要使用模板 c x![d] d c[2]
。这假设双精度浮点数必须与双精度浮点数的大小对齐。
对于对齐命令,count
为 0 等效于 count
为 1;两者都是无操作。
n
、N
、v
和 V
接受 !
修饰符来表示大端/小端字节序的带符号 16 位/32 位整数。这只有在所有共享打包数据的平台都使用相同的带符号整数二进制表示时才可移植;例如,当所有平台都使用二进制补码表示时。
可以使用 #
在模板中嵌入注释,直到行尾。空格可以将打包代码彼此分开,但修饰符和重复计数必须紧随其后。将复杂的模板分解成单独的逐行组件,并进行适当的注释,可以像 /x
对复杂的模式匹配一样,提高打包/解包格式的可读性和可维护性。
如果模板需要比 pack
给定的参数更多,pack
会假设额外的 ""
参数。如果模板需要的参数少于给定的参数,则会忽略多余的参数。
尝试将特殊的浮点数 Inf
和 NaN
(无穷大,包括负数,以及非数字)打包到打包的整数值(如 "L"
)中会导致致命错误。原因是这些特殊值根本没有合理的映射到整数。
示例
$foo = pack("WWWW",65,66,67,68);
# foo eq "ABCD"
$foo = pack("W4",65,66,67,68);
# same thing
$foo = pack("W4",0x24b6,0x24b7,0x24b8,0x24b9);
# same thing with Unicode circled letters.
$foo = pack("U4",0x24b6,0x24b7,0x24b8,0x24b9);
# same thing with Unicode circled letters. You don't get the
# UTF-8 bytes because the U at the start of the format caused
# a switch to U0-mode, so the UTF-8 bytes get joined into
# characters
$foo = pack("C0U4",0x24b6,0x24b7,0x24b8,0x24b9);
# foo eq "\xe2\x92\xb6\xe2\x92\xb7\xe2\x92\xb8\xe2\x92\xb9"
# This is the UTF-8 encoding of the string in the
# previous example
$foo = pack("ccxxcc",65,66,67,68);
# foo eq "AB\0\0CD"
# NOTE: The examples above featuring "W" and "c" are true
# only on ASCII and ASCII-derived systems such as ISO Latin 1
# and UTF-8. On EBCDIC systems, the first example would be
# $foo = pack("WWWW",193,194,195,196);
$foo = pack("s2",1,2);
# "\001\000\002\000" on little-endian
# "\000\001\000\002" on big-endian
$foo = pack("a4","abcd","x","y","z");
# "abcd"
$foo = pack("aaaa","abcd","x","y","z");
# "axyz"
$foo = pack("a14","abcdefg");
# "abcdefg\0\0\0\0\0\0\0"
$foo = pack("i9pl", gmtime);
# a real struct tm (on my system anyway)
$utmp_template = "Z8 Z8 Z16 L";
$utmp = pack($utmp_template, @utmp1);
# a struct utmp (BSDish)
@utmp2 = unpack($utmp_template, $utmp);
# "@utmp1" eq "@utmp2"
sub bintodec {
unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
}
$foo = pack('sx2l', 12, 34);
# short 12, two zero bytes padding, long 34
$bar = pack('s@4l', 12, 34);
# short 12, zero fill to position 4, long 34
# $foo eq $bar
$baz = pack('s.l', 12, 4, 34);
# short 12, zero fill to position 4, long 34
$foo = pack('nN', 42, 4711);
# pack big-endian 16- and 32-bit unsigned integers
$foo = pack('S>L>', 42, 4711);
# exactly the same
$foo = pack('s<l<', -42, 4711);
# pack little-endian 16- and 32-bit signed integers
$foo = pack('(sl)<', -42, 4711);
# exactly the same
通常,相同的模板也可以在 unpack
中使用。