Lua5.3还引入了一个在二进制数和基本类型值(数值和字符串类型)之间进行转换的函数。函数string.pack会把值“打包(pack)”为二进制字符串,而函数string.unpack则从字符串中提取这些值。
函数string.pack和函数string.unpack的第一个参数是格式化字符串,用于描述如何打包数据。格式化字符串中的每个字母都描述了如何打包/解包一个值,例如:
> s = string.pack("iii", 3, -27, 450) > #s --> 12 > string.unpack("iii", s) --> 3 -27 450 13点击复制复制失败已复制
调用函数string.pack将创建一个字符串,其中为3个整型数的二进制代码(根据"iii"),每一个"i"编码对与之对应的参数进行了编码,而字符串的长度则是一个整型数本身大小的3倍。调用函数string.unpack对给定字符串中的3歌整型数进行了解码(还是根据"iii")并返回解码后的结果。
为了便于迭代,函数string.unpack还会返回最后一个读取的元素在字符串中的位置(这解释了上面示例代码中的13)。相应的,该函数还有一个可选的第3个参数,这个参数用于指定开始读取的位置。例如,下例输出了一个指定字符串中所有被打包的字符串:
s = "hello\0Lua\0world\0" local i = 1 while i <= #s do local res res, i = string.unpack("z", s, i) print(res) end
输出:
hello Lua world
正如我们马上要看到的,选项z意味着一个以\0结尾的字符串。因此,调用函数unpack会从s中提取位于i的字符串,并返回该字符串外加循环迭代的下一个位置。
对于编码一个整型数而言有几种选项,每一种对应了一种整型大小:b(char)、h(short)、i(int)、l(long)和j(代表Lua语言中整型数的大小)。要是使用固定的、与机器无关的大小,可以在选项i后加上一个1-16的数。例如,i7会产生7字节的整型数。所有的大小都会被检查是否存在溢出的情况。
> x = string.pack("i7", 1 << 54) > string.unpack("i7", x) --> 18014398509481984 8 > x = string.pack("i7", -(1 << 54)) > string.unpack("i7", x) --> -18014398509481984 8 > x = string.pack("i7", 1 << 55) --> stdin:1: bad argument #2 to 'pack' (integer overflow)点击复制复制失败已复制
我们可以打包和解包比Lua语言原生整型更大的整型数,但是在解包的时候他们的实际值必须能够被Lua语言的整型数容纳:
> x = string.pack("i12", 2^61) > string.unpack("i12", x) --> 2305843009213693952 13 > x = "aaaaaaaaaaaa" > string.unpack("i12", x) --> stdin:1: 12-byte integer does not fit into Lua Integer点击复制复制失败已复制
每一个针对整型数的选项都有一个对应的大写版本,对应相应大小的无符号整型数:
> s = "\xFF" > string.unpack("b", s) --> -1 2 > string.unpack("B", s) --> 255 2
同时,无符号整型数对于size_t而言还有一个额外的选项T(size_t类型在IOS C中是一个足够容纳任意对象大小的无符号整型数)。
我们可以用3中表示形式打包字符串:\0结尾的字符串、定长字符串和使用显式长度的字符串。\0结尾的字符串使用选择z;定长字符串使用选项cn,其中c是被打包字符串的字节数。显式长度的字符串在存储时会在字符串前加上该字符串的长度。在这种情况下,选项的格式形如sn,其中n是用于保存字符串长度的无符号整型数的大小。例如,选项s1表示把字符串长度保存在一个字节中:
s = string.pack("s1", "hello") for i = 1, #s do print((string.unpack("B", s, i))) end
输出:
5 104 101 108 108 111
如果用于保存长度的字节容纳不了字符串长度,那么Lua语言会抛出异常。我们也可以单纯使用选项s,在这种情况下,字符串长度会被以足够容纳任何字符串长度的size_t类型保存(在64位机器中,size_t通常是8字节的无符号整型数,对于较短的字符串来说可能浪费空间)。
对于浮点型是数,有3中选项:
- f用于单精度浮点数
- d用于双精度浮点数
- n用于Lua语言浮点数
格式化字符串也有用来控制大小端模式和二进制数据对其的选项。在默认情况下,格式使用的是机器原生的大小端模式。选项>把所有后续的编码转换为大端模式或网络字节序(network byte order):
s = string.pack(">i4", 1000000) for i = 1, #s do print((string.unpack("B", s, i))) end
输出:
0 15 66 64
选项<则改为小端模式:
s = string.pack("<i2 i2", 500, 24) for i = 1, #s do print((string.unpack("B", s, i))) end
输出:
244 1 24 0
最后,选项= 改回机器默认的原生大小端模式。
对于对齐而言,选项!n强制数据对齐到以n为倍数的索引上。更准确的说,如果数据比n小,那么对齐到其