python номера пакетов / распаковки, указывающие количество байтов - PullRequest
2 голосов
/ 05 апреля 2020

Я пытаюсь преобразовать целые числа (> = 0 && <2 ^ 32, конечно) в Python в 4-байтовое представление без знака int и обратно. </p>

Насколько я понимаю, документы, указанные в struct.pack, являются стандартными, но размер не гарантируется. Как я могу убедиться, что получаю именно 4 байта?

Один способ, который я нашел с помощью ctypes:

byte_repr=bytes(ctypes.c_uint32(data))

Это самый питон c, какой есть? И каким будет обратный путь (для этого или любого другого решения)?

Ответы [ 2 ]

2 голосов
/ 05 апреля 2020

Типы int и bytes имеют методы, необходимые для этого.

Обратите внимание, что я звоню from_bytes из класса int, но его можно вызвать из 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
>>>
1 голос
/ 06 апреля 2020

Учитывая указанный интервал, вы говорите о без знака int s .
[Python 3.Docs]: struct - интерпретировать строки как упакованные двоичные данные работает нормально (хорошо, на платформах (компиляторах), где sizeof(int) == 4).
Поскольку для подавляющего большинства сред вышеприведенное верно, вы можете безопасно его использовать (если только вы не уверены, что код будет работать на платформе exoti c, где компилятор использовал для сборки 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

Связано (удаленно): [SO]: максимальное и минимальное значение C типов целых чисел от Python.

[Python 3.Docs]: ctypes - библиотека сторонних функций для 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 является более простым и более простым, так как он не включает какой-либо модуль, кроме встроенный ( [Python 3.Docs]: встроенный Типы - дополнительные методы для целочисленных типов ). В качестве примечания можно использовать sys.byteorder .

...