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

5 投票
4 回答
6661 浏览
提问于 2025-04-16 03:10

我想把一个整数(intlong)转换成一个大端字节串。这个字节串的长度要可变,这样就能只用最少的字节(因为前面的数据总长度是已知的,所以可以推测出这个可变长度)。

我现在的解决方案是

import bitstring

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

但这个方法显然依赖于机器的字节序,结果也不正确,因为没有添加0位,也没有前面加位。

有没有人知道怎么做,能不假设int的长度或字节序?

4 个回答

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

大概是这样的。还没测试(等下次编辑时再测试)。适用于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:第二种方法不假设整数已经是大端格式。看看在一个著名的小端格式环境下会发生什么:

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

撰写回答