打包和解包二进制数据

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

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小,那么对齐到其

目录
相关文章
|
8月前
|
C++ Python
哈夫曼编码实现文件的压缩和解压
哈夫曼编码实现文件的压缩和解压
108 0
|
Windows
msi文件解包
msi文件解包
1559 1
msi文件解包
|
11月前
|
Unix Linux
Linux文件打包及压缩、解包及解压
前面学习了文件的查找,这篇文章将学习Linux中文件的打包及解压、文件的解包和解压。这就是这篇文章的学习目标,学会如何打包压缩,解包解压。
208 0
fastq文件转换成fasta文件
fastq文件转换成fasta文件
221 0
|
Java
APK解包修改后,重新打包
APK解包修改后,重新打包
631 0
|
Linux
linux中所有格式的压缩包(解包打包)集合
linux中所有格式的压缩包(解包打包)集合
92 0
|
Serverless 对象存储 弹性计算
函数计算对文件进行压缩和解压缩使用总结
前言 函数计算具有弹性伸缩的能力,可以给用户带来免运维和毫秒级扩容的计算能力。 但是它也存在一些限制, 比如一个执行环境最大内存只有3G, 本文旨在进行总结一些函数计算在文件压缩和解压缩的一些实践案例, 希望能给大家抛砖引玉,引出更好的实践案例。
2889 0
|
开发工具 数据可视化 Web App开发