将Python的整数转换为大端字节串

85

我有一个非负整数,想要高效地将其转换为包含相同数据的大端字符串。例如,整数1245427(即0x1300F3)应该生成长度为3的字符串,其中包含三个字符,其字节值分别为0x13、0x00和0xf3。

我的整数在35位(十进制)数字的范围内。

我应该如何做?


1
这个回答解决了你的问题吗?在Python中将字符串从大端序转换为小端序或反之亦然 - TAbdiukov
9个回答

73

在Python 3.2及以上版本中,您可以使用int.to_bytes函数:

如果您不想指定大小

>>> n = 1245427
>>> n.to_bytes((n.bit_length() + 7) // 8, 'big') or b'\0'
b'\x13\x00\xf3'

如果您不介意指定大小

>>> (1245427).to_bytes(3, byteorder='big')
b'\x13\x00\xf3'

12
怎么在2.6版本中实现这个? - Kimvais
4
如果您的整数是有符号的,加上 signed=True - gerrit
2
我想问一下,(length + 7) // 8 这部分是不是等同于 math.ceil(length / 8)?如果是的话,使用后者会更清晰明了。 - Jezzamon
1
@JanusTroelsen 如果n.bit_length()的大小是合理的(大约需要一千万亿位才会有问题),那么它们是相同的,这意味着n将变得非常大。在我的情况下,可读性比性能提高更重要,而且数字永远不会那么大。 - Jezzamon
1
@Kebman 为什么它们应该是有效的 UTF-8?我不明白这会是一个情况。也许你可以提出一个新的写得好的问题,然后@我,我会尝试回答。 - Janus Troelsen
显示剩余5条评论

67

你可以使用 struct 模块:

import struct
print(struct.pack('>I', your_int))

'>I'是一个格式化字符串。其中>表示大端序(big endian),I表示无符号整数(unsigned int)。更多格式字符,请查看相关文档。


11
struct.pack 返回一个固定长度的字符串,似乎没有处理大整数的功能。我想我可以将我的整数分解为2^32的幂次,通过struct.pack()运行它,并重新组装结果,但这看起来很费力...你知道更简单的方法吗? - fish
2
我找不到一个处理任意长整数的库。我认为你必须自己实现它。其他答案包含了实现方法。 - Ayman Hourieh
Ayman,请注意Python内置支持任意长整数,因此您不需要库。在Python 3中,只会有“int”类型,但即使在Python 2.4+中,“int”在溢出32位(带符号)时也会自动转换为Python“long”。 - Ben Hoyt
2
benhoyt,谢谢您的评论。我意识到这一点。我是在谈论如何处理任意长整数转换为大端格式,而不是一般地处理它们。 - Ayman Hourieh

15

这种方法速度快,适用于小整数和(任意)大整数:

def Dump(n): 
  s = '%x' % n
  if len(s) & 1:
    s = '0' + s
  return s.decode('hex')
print repr(Dump(1245427))  #: '\x13\x00\xf3'

3
作为上述的一种变体,可以将 if len(s) & 1 替换为 if len(s) % 2(如果十六进制字符数是奇数,则两者都为 true),并且将 '%x' % n 替换为 '{0:x}'.format(n)(这两个都将数字格式化为十六进制字符串)。 - Abbafei

11

可能最好的方式是通过内置的struct模块

>>> import struct
>>> x = 1245427
>>> struct.pack('>BH', x >> 16, x & 0xFFFF)
'\x13\x00\xf3'
>>> struct.pack('>L', x)[1:]  # could do it this way too
'\x13\x00\xf3'

或者,虽然我通常不建议这样做,因为容易出错,但您可以通过移位和使用 chr() 函数“手动”完成:

>>> x = 1245427
>>> chr((x >> 16) & 0xFF) + chr((x >> 8) & 0xFF) + chr(x & 0xFF)
'\x13\x00\xf3'

出于好奇,为什么您只想要三个字节?通常情况下,您会将这样的整数打包成一个完整的 32 位(C 中的 unsigned long),并使用 struct.pack('>L', 1245427) 来跳过 [1:] 步骤吗?


7

这是一个基于@pts的回答的单源Python 2/3兼容版本:

#!/usr/bin/env python
import binascii

def int2bytes(i):
    hex_string = '%x' % i
    n = len(hex_string)
    return binascii.unhexlify(hex_string.zfill(n + (n & 1)))

print(int2bytes(1245427))
# -> b'\x13\x00\xf3'

7
def tost(i):
  result = []
  while i:
    result.append(chr(i&0xFF))
    i >>= 8
  result.reverse()
  return ''.join(result)

3
如果期望结果是空字符串 '\x00',则需要在 while 循环之前进行 i == 0 的测试。tost(0) 返回一个空字符串。 - Mike Ellis
使用try/except比if i==0更好,因为假设它大部分时间不会发生。 - J.J

3

我认为最简单的方法是:

import struct
val = 0x11223344
val = struct.unpack("<I", struct.pack(">I", val))[0]
print "%08x" % val

这将把一个整数转换为字节交换后的整数。


这种方法并不会对某些数字(例如70000)使用最有效的字节数。 - J.J

2
使用 bitstring 模块:
>>> bitstring.BitArray(uint=1245427, length=24).bytes
'\x13\x00\xf3'

请注意,对于这种方法,您需要指定创建的位串的位数。
尽管在内部,这与Alex的答案基本相同,但是如果您想对数据进行更多操作,则该模块具有许多额外的功能可用。

-2
非常容易使用 pwntools,这是专门为软件黑客而创建的工具。
(不带讽刺地说,我偶然发现了这个线程并尝试了这里的解决方案,直到我意识到 pwntools 中存在转换功能)
import pwntools

x2 = p32(x1)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接