Python 如何将整数转换为可变长度字节字符串?
我想将整数(Python 如何将整数转换为可变长度字节字符串?,python,integer,bit-manipulation,endianness,byte,Python,Integer,Bit Manipulation,Endianness,Byte,我想将整数(int或long)转换为大端字节字符串。字节字符串必须具有可变长度,以便只使用最小数量的字节(已知前面数据的总长度,因此可以推断可变长度) 我目前的解决办法是 import bitstring bitstring.BitString(hex=hex(456)).tobytes() 这显然取决于机器的尾数,并给出错误的结果,因为0位是附加的,没有前缀 有没有人知道这样做的方法,而不必对int的长度或长度做任何假设?类似的东西。未测试(直到下一次编辑)。对于Python2.x。假设n
int
或long
)转换为大端字节字符串。字节字符串必须具有可变长度,以便只使用最小数量的字节(已知前面数据的总长度,因此可以推断可变长度)
我目前的解决办法是
import bitstring
bitstring.BitString(hex=hex(456)).tobytes()
这显然取决于机器的尾数,并给出错误的结果,因为0位是附加的,没有前缀
有没有人知道这样做的方法,而不必对
int
的长度或长度做任何假设?类似的东西。未测试(直到下一次编辑)。对于Python2.x。假设n>0
tmp = []
while n:
n, d = divmod(n, 256)
tmp.append(chr(d))
result = ''.join(tmp[::-1])
编辑:已测试
如果您不阅读手册,但喜欢Bitbash,请尝试以下方法,而不是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'
>>>
使用
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)
如果您使用的是Python 2.7或更高版本,则可以使用
bit\u 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'
我用一行重新编写了John Machins的第二个答案,以便在我的服务器上使用:
def bytestring(n):
return ''.join([chr((n>>(i*8))&0xFF) for i in range(n.bit_length()/8,-1,-1)])
我发现第二种方法,使用位移位,对大数字和小数字都更快,而不仅仅是小数字。这只需要对
int
有效,还是对long
也有效?对于long
,我忘了这一点。我将编辑这个问题。这可以在任何版本的Python中简单地完成,而不需要外部依赖项——在任何情况下,您都需要BYTEstring,而不是位字符串。struct.pack
方法不起作用,因为struct.unpack
需要固定长度。对于其他方法,您还需要一个反向函数(平凡)。这假设1字节等于8位。我不知道您是否可以对python语义做出这样的假设。第二种方法假设整数已经是big-endian。@ott:可以很安全地说,1字节等于8位,Python整数本身没有endianness-这只是它们如何存储或传输的问题(也就是说,如果你在到达这一步之前从某个地方错误地解包了n
,这才是问题)。这两种方法在我看来都不错。实际上,它只是假设一个字节至少是8位,这是由C标准保证的,因此也是由C PyBytes类型保证的。(1)有人请给我看一台机器,它有一个非8位字节,不在博物馆里(比如Univac 110X(9位)或ICL 190X(6位)),并且有一个当前支持的Python实现(2)对于任何非负整数x
,x&0xFF
和x%256
在C和Python中的含义完全相同,而与主机的端号无关。bit\u length
似乎是一个干净的解决方案(尽管我在Debian上使用Python 2.6)(i.bit_length()+7)/8*8
将长度四舍五入为可除以8的长度,对吗?endianness问题仍然存在。我发现了一个。因此,只剩下尾端问题。uint
是uintbe
的别名,因此尾端问题也得到了解决。这比需要的要困难一些,因此我添加了一个功能请求(),希望在下一个版本中,您可以只说BitString(uintbe=456)。bytes
)我在使用大整数时遇到了一个错误。e、 g.big=244232342342432343422335353=>TypeError:“float”对象不能解释为整数