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

发布于 2024-09-15 18:18:24 字数 327 浏览 6 评论 0原文

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

我当前的解决方案是

import bitstring

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

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

有谁知道一种方法可以在不对 int 的长度或字节顺序做出任何假设的情况下做到这一点?

I want to convert an integer (int or long) a big-endian byte string. The byte string has to be of variable length, so that only the minimum number of bytes are used (the total length length of the preceding data is known, so the variable length can be inferred).

My current solution is

import bitstring

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

Which obviously depends on the endianness of the machine and gives false results, because 0 bits are append and no prepended.

Does any one know a way to do this without making any assumption about the length or endianess of an int?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

谁人与我共长歌 2024-09-22 18:18:24

像这样的东西。未经测试(直到下次编辑)。对于 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'
>>>

Something like this. Untested (until next edit). For Python 2.x. Assumes n > 0.

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

Edit: tested.

If you don't read manuals but like bitbashing, instead of the divmod caper, try this:

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

Edit 2: If your numbers are relatively small, the following may be faster:

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

Edit 3: The second method doesn't assume that the int is already bigendian. Here's what happens in a notoriously littleendian environment:

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'
>>>
以往的大感动 2024-09-22 18:18:24

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

A solution using struct and itertools:

>>> import itertools, struct
>>> "".join(itertools.dropwhile(lambda c: not(ord(c)), struct.pack(">i", 456))) or chr(0)
'\x01\xc8'

We can drop itertools by using a simple string strip:

>>> struct.pack(">i", 456).lstrip(chr(0)) or chr(0)
'\x01\xc8'

Or even drop struct using a recursive function:

def to_bytes(n): 
    return ([chr(n & 255)] + to_bytes(n >> 8) if n > 0 else [])

"".join(reversed(to_bytes(456))) or chr(0)
原野 2024-09-22 18:18:24

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

If you're using Python 2.7 or later then you can use the bit_length method to round the length up to the next byte:

>>> i = 456
>>> bitstring.BitString(uint=i, length=(i.bit_length()+7)/8*8).bytes
'\x01\xc8'

otherwise you can just test for whole-byteness and pad with a zero nibble at the start if needed:

>>> s = bitstring.BitString(hex=hex(i))
>>> ('0x0' + s if s.len%8 else s).bytes
'\x01\xc8'
一抹苦笑 2024-09-22 18:18:24

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

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

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

I reformulated John Machins second answer in one line for use on my server:

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

I have found that the second method, using bit-shifting, was faster for both large and small numbers, and not just small numbers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文