打包和解包二进制数据

简介: 打包和解包二进制数据

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中选项:

  1. f用于单精度浮点数
  2. d用于双精度浮点数
  3. 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小,那么对齐到其

目录
相关文章
|
Windows
msi文件解包
msi文件解包
1737 1
msi文件解包
|
2月前
|
存储 Python
使用 struct 模块打包/解包二进制数据
使用 struct 模块打包/解包二进制数据
48 2
|
3月前
|
Python
|
6月前
|
Go
golang读取网络字节并解压zip
golang读取网络字节并解压zip
46 0
|
6月前
1078 字符串压缩与解压 (20 分)
1078 字符串压缩与解压 (20 分)
|
JSON 前端开发 JavaScript
前端下载二进制流文件
前端下载二进制流文件
351 0
|
Unix Linux
Linux文件打包及压缩、解包及解压
前面学习了文件的查找,这篇文章将学习Linux中文件的打包及解压、文件的解包和解压。这就是这篇文章的学习目标,学会如何打包压缩,解包解压。
288 0
fastq文件转换成fasta文件
fastq文件转换成fasta文件
282 0
|
Java
APK解包修改后,重新打包
APK解包修改后,重新打包
699 0
|
Linux
linux中所有格式的压缩包(解包打包)集合
linux中所有格式的压缩包(解包打包)集合
113 0