Как преобразовать целое число в строку байтов переменной длины?
Я хочу преобразовать целое число (int
или же long
) байтовая строка с прямым порядком байтов. Строка байтов должна иметь переменную длину, чтобы использовалось только минимальное количество байтов (общая длина предыдущих данных известна, поэтому можно определить переменную длину).
Мое текущее решение
import bitstring
bitstring.BitString(hex=hex(456)).tobytes()
Что, очевидно, зависит от порядкового номера машины и дает ложные результаты, потому что 0 бит добавляются, а не добавляются.
Кто-нибудь знает способ сделать это, не делая никаких предположений о длине или бесконечности int
?
4 ответа
Если вы используете Python 2.7 или более поздней версии, вы можете использовать bit_length
метод округления длины до следующего байта:
>>> i = 456
>>> bitstring.BitString(uint=i, length=(i.bit_length()+7)/8*8).bytes
'\x01\xc8'
в противном случае вы можете просто проверить целостность байта и заполнить его нулевым кусочком в начале, если это необходимо:
>>> s = bitstring.BitString(hex=hex(i))
>>> ('0x0' + s if s.len%8 else s).bytes
'\x01\xc8'
Что-то вроде этого. Не проверено (до следующего редактирования). Для Python 2.x. Предполагается, что n > 0.
tmp = []
while n:
n, d = divmod(n, 256)
tmp.append(chr(d))
result = ''.join(tmp[::-1])
Редактировать: проверено.
Если вы не читаете руководства, но любите битбэшировать, вместо divmod
Капер, попробуйте это:
d = n & 0xFF; n >>= 8
Редактировать 2: Если ваши цифры относительно малы, следующее может быть быстрее:
result = ''
while n:
result = chr(n & 0xFF) + result
n >>= 8
Редактировать 3: Второй метод не предполагает, что int уже является бигендовым. Вот что происходит в общеизвестно маленькой среде:
Python 2.7 (r27:82525, Jul 4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> n = 65539
>>> result = ''
>>> while n:
... result = chr(n & 0xFF) + result
... n >>= 8
...
>>> result
'\x01\x00\x03'
>>> import sys; sys.byteorder
'little'
>>>
Решение с использованием struct
а также itertools
:
>>> import itertools, struct
>>> "".join(itertools.dropwhile(lambda c: not(ord(c)), struct.pack(">i", 456))) or chr(0)
'\x01\xc8'
Мы можем бросить itertools
с помощью простой строки:
>>> struct.pack(">i", 456).lstrip(chr(0)) or chr(0)
'\x01\xc8'
Или даже падение struct
используя рекурсивную функцию:
def to_bytes(n):
return ([chr(n & 255)] + to_bytes(n >> 8) if n > 0 else [])
"".join(reversed(to_bytes(456))) or chr(0)
Я переформулировал второй ответ Джона Мачинса в одну строку для использования на моем сервере:
def bytestring(n):
return ''.join([chr((n>>(i*8))&0xFF) for i in range(n.bit_length()/8,-1,-1)])
Я обнаружил, что второй метод, использующий сдвиг битов, был быстрее для больших и малых чисел, а не только для маленьких.