【问题标题】:Convert a Python int into a big-endian string of bytes将 Python int 转换为大端字节串
【发布时间】:2009-05-10 20:24:01
【问题描述】:

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

我的整数是 35 (base-10) 位数。

我该怎么做?

【问题讨论】:

标签: python


【解决方案1】:

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

【讨论】:

  • 如何在 2.6 中做到这一点?
  • 如果您的整数有符号,请添加signed=True
  • 我认为(length + 7) // 8 部分与math.ceil(length / 8) 相同吗?如果是这样,我认为使用该选项会更清楚
  • @JanusTroelsen 它们是相同的,只要n.bit_length() 的大小合理(您需要大约千万亿位才能出现问题,这意味着n 将是难以置信的大)。在我的情况下,可读性比性能提升更重要,而且数字永远不会那么大。
  • @Kebman 为什么它们是有效的 UTF-8?我不明白为什么会这样。也许问一个新的写得很好的问题并联系我,我会尽力回复。
【解决方案2】:

您可以使用struct 模块:

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

'>I' 是一个格式字符串。 > 表示大端,I 表示无符号整数。查看文档以获取更多格式字符。

【讨论】:

  • struct.pack 返回一个固定长度的字符串,并且似乎没有处理大整数的设施。我想我可以将我的 int 分解为 2^32 的幂,通过 struct.pack() 运行它,然后重新组合结果,但这似乎需要做很多工作......你知道更简单的方法吗?
  • 我找不到处理任意长整数的库。我认为您将不得不自己实施它。其他答案包含实现。
  • Ayman,请注意 Python 内置了对任意长整数的支持,因此您不需要库。在 Python 3 中,只会有 int 类型,但即使在 Python 2.4+ 中,ints 在溢出 32 位(有符号)时也会自动转换为 Python longs。
  • 本霍伊特,感谢您的评论。我知道这一点。我说的是处理任意长整数到大端的转换。一般不处理它们。
【解决方案3】:

这速度很快,适用于小型和(任意)大型整数:

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

【讨论】:

  • 作为上述的一种变体,可以将if len(s) & 1 替换为if len(s) % 2(如果有奇数个十六进制字符,则两者都是真的),并且'%x' % n 替换为'{0:x}'.format(n)(两者都是其中将数字格式化为十六进制字符串)。
【解决方案4】:

可能最好的方法是通过内置的struct module

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

或者——我通常不推荐这个,因为它容易出错——你可以通过 shift 和 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:] 步骤?

【讨论】:

    【解决方案5】:
    def tost(i):
      result = []
      while i:
        result.append(chr(i&0xFF))
        i >>= 8
      result.reverse()
      return ''.join(result)
    

    【讨论】:

    • tost(0) 返回一个空字符串。如果期望的结果是 '\x00',则需要在 while 循环之前进行 i == 0 测试
    • 使用 try/except 会比 if i==0 更好,前提是它在大多数情况下都不会发生。
    【解决方案6】:

    基于@pts' answer的单源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】:

      我认为最短的方法如下:

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

      这会将整数转换为字节交换整数。

      【讨论】:

      • 这只不会对某些数字使用最有效的字节数,例如70000
      【解决方案8】:

      使用bitstring 模块:

      >>> bitstring.BitArray(uint=1245427, length=24).bytes
      '\x13\x00\xf3'
      

      请注意,对于此方法,您需要指定您正在创建的位串的长度。

      在内部,这与 Alex 的回答几乎相同,但如果您想对数据做更多事情,该模块还有很多额外的功能可用。

      【讨论】:

        【解决方案9】:

        使用 pwntools 非常容易,这是为软件黑客创建的工具

        (讽刺的是,我偶然发现了这个线程并在这里尝试了解决方案,直到我意识到 pwntools 中存在转换功能)

        import pwntools
        
        x2 = p32(x1)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-20
          • 2016-03-04
          • 2016-02-20
          相关资源
          最近更新 更多