Python3-struct报错-argument-for-s-must-be-a-bytes-object

4次阅读

共计 2235 个字符,预计需要花费 6 分钟才能阅读完成。

在用 python3 进行文件打包二进制数据的存储与解析过程中使用 struct 模块运行下面代码发生错误:

F = open(‘data.bin’, ‘wb’)
import struct
data = struct.pack(‘>i4sh’, 7, ‘spam’, 8)
data

报错信息如下:

error                                     Traceback (most recent call last)
<ipython-input-12-9b7a8e6cf48b> in <module>
1 F = open(‘data.bin’, ‘wb’)
2 import struct
—-> 3 data = struct.pack(‘>i6sh’, 7, ‘spam’, 8)
4 data

error: argument for ‘s’ must be a bytes object

先说解决方案:格式化字符串的值在 python 的类型是 bytes 类型,而 python3 中所有文本都是 Unicode,所以需要转换为 bytes 类型,在 ’spam’ 前面加’b’ 进行转换。

F = open(‘data.bin’, ‘wb’)
import struct
data = struct.pack(‘>i4sh’, 7, b’spam’, 8)
data

输出结果是:

b’\x00\x00\x00\x07spam\x00\x08′

python3 新增的 bytes 类型

在 python2 中字节类型和字符类型区别不大,但是在 python3 中最重要的特性是对文本和二进制数据做了更加清晰的区分。

文本总是 Unicode, 由字符类型表示,而二进制数据则由 bytes 类型表示。

python3 不会以任意隐式方式混用字节型和字符型,也因此在 python3 中不能拼接字符串和字节包(python2 中可以,会自动进行转换),也不能在字节包中搜索字符串,也不能将字符串传入参数为字节包的函数。

需要注意的是,在网络数据传输过程中,python2 可以通过字符串 (string) 方式传输,但是 python3 只能通过二进制 (bytes) 方式来传输,因此要对传输文本进行转换。

转化方式:

str → byte 用 encode()方法 byte → str 用 decode()方法

中文字符串转二进制:

‘ 你好 ’.encode(‘utf-8’)

输出:

b’\xe4\xbd\xa0\xe5\xa5\xbd’

二进制转回字符串:

b’\xe4\xbd\xa0\xe5\xa5\xbd’.decode(‘utf-8’)

输出:’ 你好 ’

英文字符串转二进制

‘hello world’.encode(‘utf-8’)

输出:

b’hello world’

encode()和 decode()方法中 默认编码 为 utf-8,但是为了避免错误,最好将编码加上。

仅仅知道加 ’b’ 可以解决问题但是感觉还是不够,’>i4sh’ 看不懂,所以去官方文档查 struct 看到结果如下:

struct.pack(format, v1, v2, …) 返回一个 bytes 对象,其中包含根据格式字符串 format 打包的值 v1, v2, … 参数个数必须与格式字符串所要求的值完全匹配。

可以看出 i 对应的是 python 中的整数,s 对应的是字符串,h 对应的是整数。

格式字符之前可以带有整数重复计数。例如,格式字符串 '4h' 的含义与 'hhhh' 完全相同。

所以测试了一下,在 h 前加数字 2:

F = open(‘data.bin’, ‘wb’)
import struct
data = struct.pack(‘>i4s2h’, 7, b’spam’, 8)
data

报错如下:

—————————————————————————
error                                     Traceback (most recent call last)
<ipython-input-33-a27281e58db6> in <module>
1 F = open(‘data.bin’, ‘wb’)
2 import struct
—-> 3 data = struct.pack(‘>i4s2h’, 7, b’spam’, 8)
4 data

error: pack expected 4 items for packing (got 3)

也是就说 2h 就需要在 s 后面有 2 个整数参数,但是 ’4s’ 却不是需要四个 bytes 参数。

对于 ‘s’ 格式字符,计数会被解析为字节的长度,而不是像其他格式字符那样的重复计数;例如,’10s’ 表示一个 10 字节的字节串,而 ’10c’ 表示 10 个字符。如果未给出计数,则默认值为 1。对于打包操作,字节串会被适当地截断或填充空字节以符合要求。

所以做了个测试,先后将 s 前的数字从 1 慢慢涨到 6 得到的输出结果如下:

b’\x00\x00\x00\x07s\x00\x08′               #’>i1sh’
b’\x00\x00\x00\x07sp\x00\x08′             #’>i2sh’
b’\x00\x00\x00\x07spa\x00\x08′             #’>i3sh’
b’\x00\x00\x00\x07spam\x00\x08′           #’>i4sh’
b’\x00\x00\x00\x07spam\x00\x00\x08′       #’>i5sh’
b’\x00\x00\x00\x07spam\x00\x00\x00\x08′   #’>i6sh’

在数字小于后面字节长度时会截取,超过长度时会在后面填充空字节。

正文完
 0