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 的值介于 0128 之间。

例子:

一个字节值 65 (hex 0x41)将表示为 'A'(因为 AASCII 代码是 65)

一个字节值 217 (hex 0xd9)将简单地表示为 '\xd9'(没有可打印的 char 与此 ASCII 代码)

在进一步讨论之前,需要介绍一下字节序:这就是数据(在我们的例子中是数字)在计算机内存中的表示方式。几个链接(尽管可以在互联网上找到很多资源):

[Wikipedia]: Endianness [UMD.CS]: Big and Little Endian

我将尝试简要解释 biglittle 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强>。

现在,回到我们的问题:如上所述,50x05(或 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表示为:0xd9030x0000d903

如果还没有意义,请使用这个稍微修改过的代码版本并使用它,为 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 / 串行通信问题

python struct.pack() 二进制文件,文件中打包二进制数据的存储与解析