Шаг 49.
Python: сборник рецептов. Числа, даты и время. Упаковка и распаковка больших целых чисел из байтовых строк

    На этом шаге мы рассмотрим некоторые методы, которые можно использовать для решения этой задачи.

Задача

    У вас есть строка байтов, и вам нужно распаковать ее в целочисленное значение. Или же вам нужно конвертировать большое целое число в байтовую строку.

Решение

    Предположим, ваша программа должна работать с байтовой строкой из 16 элементов, которая содержит 128-битное целочисленное значение. Например:

>>> data = b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
>>> 

    Чтобы перевести байты в целое число, используйте int.from_bytes(), определив порядок следования байтов таким образом:

>>> len(data)
16
>>> int.from_bytes(data, 'little')
69120565665751139577663547927094891008
>>> int.from_bytes(data, 'big')
94522842520747284487117727783387188
>>> 

    Чтобы преобразовать большое целочисленное значение обратно в байтовую строку, используйте метод int.to_bytes(), определив количество байтов и порядок их следования. Например:

>>> x = 94522842520747284487117727783387188
>>> x.to_bytes(16, 'big')
b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
>>> x.to_bytes(16, 'little')
b'4\x00#\x00\x01\xef\xcd\x00\xab\x90x\x00V4\x12\x00'
>>> 


Обсуждение

    Преобразование больших целочисленных значений из и в байтовые строки - не самая обычная операция. Однако иногда такая задача возникает в областях типа криптографии или работы с сетью. Например, сетевые адреса IPv6 представлены 128-битными целыми числами. Если вы пишете программу, в которой нужно вытягивать такие значения из данных, то можете столкнуться с этой задачей.

    В качестве альтернативы вы можете попытаться распаковывать значения, используя модуль struct. Это работает, но размер целых чисел, которые могут быть распакованы с помощью struct, ограничен. Поэтому вам понадобится распаковывать несколько значений и объединять их для создания итогового значения. Например:

>>> data
b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
>>> import struct
>>> hi, lo = struct.unpack('>QQ', data)
>>> (hi << 64) + lo
94522842520747284487117727783387188
>>> 

    Определение порядка следования байтов (little или big) просто указывает, записаны ли байты, из которых составляется целое число, в порядке от старшего к младшему или наоборот. Это легко понять, рассмотрев пример такого специально составленного шестнадцатеричного значения:

>>> x = 0x01020304
>>> x.to_bytes(4, 'big')
b'\x01\x02\x03\x04'
>>> x.to_bytes(4, 'little')
b'\x04\x03\x02\x01'
>>> 

    Если вы хотите упаковать целое число в строку байтов, но оно не поместится, вы получите ошибку. При необходимости вы можете использовать метод int.bit_ length(), чтобы определить, сколько байтов потребуется для хранения значения:

>>> x = 523 ** 23
>>> x
335381300113661875107536852714019056160355655333978849017944067
>>> x.to_bytes(16, 'little')
Traceback (most recent call last):
  .   .   .   .
OverflowError: int too big to convert
>>> x.bit_length()
208
>>> nbytes, rem = divmod(x.bit_length(), 8)
>>> if rem:
	nbytes += 1

	
>>> x.to_bytes(nbytes, 'little')
b'\x03X\xf1\x82iT\x96\xac\xc7c\x16\xf3\xb9\xcf\x18\xee\xec\x91\xd1\x98\xa2\xc8\xd9R\xb5\xd0'
>>> 

    На следующем шаге мы рассмотрим вычисления с комплексными числами.




Предыдущий шаг Содержание Следующий шаг