如何将整数转换为可变长度的字节串?

时间:2010-08-23 12:06:21

标签: python integer bit-manipulation endianness byte

我想将整数(intlong)转换为大端字节字符串。字节字符串必须是可变长度的,因此只使用最小字节数(前面数据的总长度已知,因此可以推断出可变长度)。

我目前的解决方案是

import bitstring

bitstring.BitString(hex=hex(456)).tobytes()

这显然取决于机器的字节顺序并给出错误的结果,因为0位是附加的而没有前置。

有没有人知道如何做到这一点,而不对int的长度或结尾做出任何假设?

4 个答案:

答案 0 :(得分:6)

像这样的东西。未经测试(直到下次编辑)。对于Python 2.x.假设n> 0

tmp = []
while n:
    n, d = divmod(n, 256)
    tmp.append(chr(d))
result = ''.join(tmp[::-1])

编辑:测试。

如果您没有阅读手册,而是喜欢bitbashing,而不是divmod caper,请尝试以下操作:

d = n & 0xFF; n >>= 8

编辑2:如果您的数字相对较小,以下内容可能会更快:

result = ''
while n:
    result = chr(n & 0xFF) + result
    n >>= 8

编辑3:第二种方法不假设int已经是bigendian。以下是臭名昭着的小环境中发生的事情:

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'
>>>

答案 1 :(得分:1)

使用structitertools的解决方案:

>>> 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)

答案 2 :(得分:0)

如果您使用的是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'

答案 3 :(得分:0)

我在一行中重新制定了John Machins的第二个答案,以便在我的服务器上使用:

def bytestring(n):
    return ''.join([chr((n>>(i*8))&0xFF) for i in range(n.bit_length()/8,-1,-1)])

我发现使用位移的第二种方法对于大数和小数都更快,而不仅仅是小数。