python pack/unpack 数字指定字节数

python pack/unpack numbers specifyig number of bytes

我正在尝试将 Python 中的整数(>=0 && <2^32 当然)转换为 4 字节无符号整数表示并返回。

根据我对文档的理解,struct.pack 中给出的大小是标准大小,但不能保证大小。我怎样才能确保我得到 4 个字节?

我发现的一种使用 ctypes 的方法:

byte_repr=bytes(ctypes.c_uint32(data))

这是最pythonic的吗?返回的方式是什么(对于这个或任何其他解决方案)?

类型 intbytes 具有您需要的方法。

请注意,我是从 class int 调用 from_bytes 但它可以从 int 实例对象调用:

>>> a = 2**32-1
>>> a.to_bytes(4, 'little')
b'\xff\xff\xff\xff'
>>> b = a.to_bytes(4, 'little')
>>> c = int.from_bytes(b, 'little')
>>> c
4294967295
>>> a
4294967295
>>>

考虑到上述间隔,您说的是 unsigned ints
[Python 3.Docs]: struct - Interpret strings as packed binary data 工作正常(好吧,在 sizeof(int) == 4 的平台(编译器)上)。
由于对于绝大多数环境来说,上述情况都是正确的,您可以安全地使用它(除非您确定代码将 运行 在异国情调的平台上使用,编译器用于构建 Python不一样)。

>>> import struct
>>>
>>> bo = "<"  # byte order: little endian
>>>
>>> ui_max = 0xFFFFFFFF
>>>
>>> ui_max
4294967295
>>> buf = struct.pack(bo + "I", ui_max)
>>> buf, len(buf)
(b'\xff\xff\xff\xff', 4)
>>>
>>> ui0 = struct.unpack(bo + "I", buf)[0]
>>> ui0
4294967295
>>>
>>> i0 = struct.unpack(bo + "i", buf)[0]  # signed int
>>> i0
-1
>>> struct.pack(bo + "I", 0)
b'\x00\x00\x00\x00'
>>>
>>> struct.pack(bo + "I", ui_max + 1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
struct.error: argument out of range
>>>
>>> struct.unpack(bo + "I", b"1234")
(875770417,)
>>>
>>> struct.unpack(bo + "I", b"123")  # 3 bytes buffer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
struct.error: unpack requires a buffer of 4 bytes
>>>
>>> struct.unpack(bo + "I", b"12345")  # 5 bytes buffer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
struct.error: unpack requires a buffer of 4 bytes

相关(远程):.

[Python 3.Docs]: ctypes - A foreign function library for Python 变体:

>>> # Continuation of previous snippet
>>> import ctypes as ct
>>>
>>> ct_ui_max = ct.c_uint32(ui_max)
>>>
>>> ct_ui_max
c_ulong(4294967295)
>>>
>>> buf = bytes(ct_ui_max)
>>> buf, len(buf)
(b'\xff\xff\xff\xff', 4)
>>>
>>> ct.c_uint32(ui_max + 1)
c_ulong(0)
>>>
>>> ct.c_uint32.from_buffer_copy(buf)
c_ulong(4294967295)
>>> ct.c_uint32.from_buffer_copy(buf + b"\x00")
c_ulong(4294967295)
>>> ct.c_uint32.from_buffer_copy(b"\x00" + buf)  # 0xFFFFFF00 (little endian)
c_ulong(4294967040)
>>>
>>> ct.c_uint32.from_buffer_copy(buf[:-1])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Buffer size too small (3 instead of at least 4 bytes)

注意:@progmatico 的回答更简单直接,因为它不涉及除 builtin 之外的任何模块([Python 3.Docs]: Built-in Types - Additional Methods on Integer Types).作为旁注,可以使用 sys.byteorder