Python struct.pack() 行为
Posted
技术标签:
【中文标题】Python struct.pack() 行为【英文标题】:Python struct.pack() behavior 【发布时间】:2016-06-23 11:15:24 【问题描述】:data = 5
Result1 = struct.pack("<L", data)
-
整数数据转换为长整数(64 位)。那是
01000000 00010100 00000000 00000000 00000000 00000000 00000000 00000000
?
然后将这些位反转为字节并作为字节字符串存储在Result1
中?那是00000000 00000000 00000000 00000000 00000000 00000000 00010100 01000000
这是该代码究竟发生了什么还是我误解了什么?
【问题讨论】:
print repr(Result1)
(在它初始化之后)你会看到实际发生了什么。
@CristiFati
with data 5
it's '\x05\x00\x00\x00'
and with data 55555
it's '\x03\xd9\x00\x00'
那么,这些是字节串吗?那么该代码究竟做了什么来获取这些字节字符串呢?它是否将 55555
读为整数并转换为 bytes
?这些看起来像是带有/x
前缀的十六进制数字,但我仍然不明白其中发生了什么,例如代码是如何计算出'\x03'
的?我真的需要一步一步手动完成。
这能回答你的问题吗?
【参考方案1】:
来自[Python 2.Docs]: struct - Interpret bytes as packed binary data:
此模块执行 Python 值和表示为 Python 字符串的 C 结构之间的转换。
这意味着它将把参数的内存表示打印为 char 序列。内存(以及其中的所有内容)是一个字节序列。每个字节都有一个值 [0..255](为简单起见,我使用 unsigned)。 因此,当它表示一个字节时,它将首先搜索一个 ASCII 代码与字节值匹配的 char,如果这样的 ( printable) char 找到,它将是那个字节的表示,否则表示将是 前面的字节值(在 hex 中) \x(表示不可打印的字符的约定)。作为旁注,(非扩展)ASCII char 的值介于 0 和 128 之间。
例子:
一个字节值 65 (hex 0x41)将表示为 'A'(因为 A 的 ASCII 代码是 65)
一个字节值 217 (hex 0xd9)将简单地表示为 '\xd9'(没有可打印的 char 与此 ASCII 代码)
在进一步讨论之前,需要介绍一下字节序:这就是数据(在我们的例子中是数字)在计算机内存中的表示方式。几个链接(尽管可以在互联网上找到很多资源):
[Wikipedia]: Endianness [UMD.CS]: Big and Little Endian我将尝试简要解释 big 和 little endian 之间的区别(再次,为简单起见,我将坚持使用 8 位原子 仅元素大小)。
假设我们正在一张纸上做一些记忆表示练习,或者更好:在黑板上。如果我们将黑板表示为计算机内存,那么左上角将是它的开始(地址 0),并且地址会随着我们向右移动而增加(当我们到达右边缘时,也向下到下一行)。我们希望将数字 0x12345678 表示为 4 字节数,从左上角开始(每个字节由正好2个十六进制数字组成):
╔═══════════╦══════════╦══════════╦══════════╦══════════╗ ║ Byte ║ 01 ║ 02 ║ 03 ║ 04 ║ ╠═══════════╬══════════╬══════════╬══════════╬══════════╣ ║ Value ║ 0x12 ║ 0x34 ║ 0x56 ║ 0x78 ║ ╚═══════════╩══════════╩══════════╩══════════╩══════════╝
我们数字的最重要字节存储在最低内存地址(以及最小有效字节存储在最高),即big endian。对于little endian,我们的字节数是相反的。
作为结论,人类认为“big endianly”。
我想讨论的另一个主题是:types(更准确地说是int)。 Python,基于 C,继承了它的原生类型,所以 int 将有 4 个字节(在某些平台上/ 可能有 8 的架构)。所以,一个int(同样是关于unsigned)有一个值[0..4294967295]。但即使对于较小的值:例如 5(只需要 1 个字节),它仍然会占用 4 个字节:(最重要的) 未使用的字节将用 0 填充。因此,我们作为 4 字节 unsigned int 的数字将是 (hex):0x00000005强>。
现在,回到我们的问题:如上所述,5 是 0x05(或 0x00000005 - 4 字节 unsigned int)或 chars em>:“\x00\x00\x00\x05”。但它与 struct.pack 显示的顺序相反;我想你已经猜到原因了:它是 little endian 表示。这是由给出的 1st (fmt) 参数(更准确地说是“”部分)给出的到[Python 2.Docs]: struct.pack(fmt, v1, v2, ...)(可能的值列在同一页上:[Python 2.Docs]: struct - Byte Order, Size, and Alignment)。 对于 55555,情况是一样的。它的hex表示为:0xd903或0x0000d903。
如果还没有意义,请使用这个稍微修改过的代码版本并使用它,为 data_set 输入不同的值并查看输出:
code.py:
import struct
fmt = "<L"
data_set = [5, 55555, 0x12345678]
for data in data_set:
output_str = " - ".format(hex(data), repr(struct.pack(fmt, data)).strip("'")) # This is just for formatting output string to be displayed to the user
print(output_str) # Python3 compatible (however the formatting above won't behave nicely)
输出:
c:\Work\Dev\***\q037990060>"C:\Install\x64\HPE\OPSWpython\2.7.10__00\python.exe" "code.py" 0x5 - \x05\x00\x00\x00 0xd903 - \x03\xd9\x00\x00 0x12345678 - xV4\x12
【讨论】:
以上是关于Python struct.pack() 行为的主要内容,如果未能解决你的问题,请参考以下文章
Python学习——struct模块的pack unpack示例
msgpack可以提供更好的性能和相同的python的struct.pack()功能吗?
msgpack 能否提供更好的性能和与 python 的 struct.pack() 相同的功能?
UDP python to c - 打包二进制数据 - struct.pack('<ff',
Arduino 和 Python (3.x) 之间的 Serial.read() 和 Struct.pack / 串行通信问题