将数字转换为二进制字符串

4

这是将Python数字转换为十六进制字符串的最佳方法吗?

number = 123456789
hex(number)[2:-1].decode('hex')

有时当你把1234567890转换为十六进制时,会出现Odd-length string的错误。
澄清一下:
我是从整数转换为十六进制。
此外,我需要进行转义。
例如: 1234567890 -> '\x49\x96\x02\xd2' 而不是 '499602D2'
另外,它需要能够接受任何Python整数。例如,一些大于Int的数字。 编辑: 到目前为止,这是我从Paolo和Devin的帖子中拼凑出的最佳解决方案。
def hexify(num):
    num = "%x" % num

    if len(num) % 2:
        num = '0'+num

    return num.decode('hex')

1
你根本不想将数字转换成十六进制字符串;你想要将它转换为二进制表示,或base256。 - Miles
1
哎呀,"十六进制字符串"在这里真的不是正确的术语。 - user3850
7个回答

6
您可以使用字符串格式化来实现:

字符串格式化

>>> number = 123456789
>>> hex = "%X" % number
>>> hex
'75BCD15'

好的,现在我该如何使它输出“\x75\xbc\d1\x05”? - Unknown
你可以使用小写的 x 来显示为小写,但我不确定另一部分是什么。 - Paolo Bergantino
好的,hex(number)[2:-1] .decode('hex')可以做到这一点,但有时会出错。 - Unknown
我非常确定你的意思是[2:],而不是[2:-1]——后者会截掉最后一位数字。 - Ben Blank
本:噢,没错,这是另一个问题,有时hex()会在末尾添加“L”。 - Unknown
希望您会喜欢我的答案,它不使用hex(),因此没有那个问题。 :-) - Ben Blank

5

我不确定你需要什么,但是你是否看过struct模块?

假设

>>> hex(123456789)
'0x75bcd15'

您可以做:

>>> struct.pack('i', 123456789)
'\x15\xcd[\x07'

注意,'\x5b' == '['

此外,您可以反转字节序:

>>> struct.pack('>i', 123456789)
'\x07[\xcd\x15'

编辑:我不确定您所说的“比long还大”的意思,因为据我所知,Python中的长整型(longs)是无界限的(除了内存)。但是,您可以通过分割和连接来处理更大的整数。例如,给定:

>>> n = 123456789012345678901234567890

翻译目标:

>>> hex(n)
'0x18ee90ff6c373e0ee4e3f0ad2L'

所以:
>>> s = ''
>>> while n >= 2**32:
...  n, r = divmod(n, 2**32)
...  s = struct.pack('>l', r) + s
... 
>>> s = struct.pack('>l', n) + s

请看hex(n)的结果是否与s相匹配:
>>> s
'\x00\x00\x00\x01\x8e\xe9\x0f\xf6\xc3s\xe0\xeeN?\n\xd2'

我需要一个能够处理比Int或long更大的数字的东西。 - Unknown

1

1
有时候当你输入1234567890时,它会报错并抱怨字符串长度为奇数。
这是因为这样做没有意义。你如何将“AAB”放入一个只能容纳2或4个数字的空间中?每个字节都是两个十六进制字符。当你有一个奇数个十六进制字符时,所需的结果是模棱两可的。你想让它等同于0AAB还是AAB0?如果你知道你想要它等同于哪一个,只需在解码之前将该字符添加到正确的位置即可。
例如:(('0' + foo) if len(foo) % 2 else foo).decode('hex') 其中foo是由%x返回的形式字符串。

错误的方式。我试图从十进制转换为十六进制,而不是从十六进制转换为十进制。 - Unknown
抱歉,我既读错了又记错了一些东西。现在答案已经改变了。 - Devin Jeanpierre
(对我来说)在数字前面加一个0更有意义。请看我的回答。如果喜欢在数字后面加空格,代码可以轻松调整。 - Stephan202
你的回答也没有解决他的问题。它返回 '\x0F' 而不是 '\x0F'。真正的答案只需要一行代码-- (('0' + foo) if len(foo) % 2 else foo).decode('hex') - Devin Jeanpierre
@Devin 我(错误地?)假设他想要在字符串中有一个字面上的反斜杠,而不是实际的十六进制值。 - Stephan202
谁知道呢。反正我已经放弃这个问题了。 - Devin Jeanpierre

0
正如Paolo所提到的,字符串格式化是正确的方法。请注意,您可以选择小写或大写字母:
>>> hex = lambda n: '%X' % n
>>> hex(42)
'2A'
>>> hex = lambda n: '%x' % n
>>> hex(42)
'2a'
>>> def escaped(n):
...     s = hex(n)
...     if len(s) & 1:
...          s = '0' + s
...     return ''.join(chr(int(s[i:i + 2], 16)) for i in range(0, len(s), 2))
...
>>> escaped(123)
'{'
>>> escaped(1234)
'\x04\xd2'
>>> escaped(12348767655764764654764445473245874398787989879879873)
'!\x01^\xa4\xdf\xdd(l\x9c\x00\\\xfa*\xf3\xb4\xc4\x94\x98\xa9\x03x\xc1'

请注意,escaped在十六进制数字为奇数时会添加前导零。该解决方案适用于任意长度的字符串。

是的,我知道,但我需要将其转换为'\x2a'而不是'2a'。 - Unknown
感谢您的输入。它几乎与我刚刚创建的代码片段相同,但我不想让它被转义,所以我想需要使用char()而不是''.join('\x")。 - Unknown

0
如果您知道输出字符串的长度,那么字符串格式化就可以解决问题。例如,要获取四个字符的字符串,您需要设置格式化长度为八:
>>> "{0:08x}".format(123456789).decode("hex")
'\x07[\xcd\x15'
>>> "{0:08x}".format(1234567890).decode("hex")
'I\x96\x02\xd2'

如果您的数字没有“填满”字符串,这将在前面添加零。例如,对于六个字符的字符串:

>>> "{0:012x}".format(123456789).decode("hex")
'\x00\x00\x07[\xcd\x15'
>>> "{0:012x}".format(1234567890).decode("hex")
'\x00\x00I\x96\x02\xd2'

编辑:

要“检测”目标字符串的长度,您可以使用math.log函数:

>>> def int2str(n):
        l = int(math.ceil(math.log(n, 256) / 2) * 4)
        return ("{0:0{1}x}").format(n, l).decode("hex")

>>> int2str(123456789)
'\x07[\xcd\x15'
>>> int2str(1234567890)
'I\x96\x02\xd2'

有没有一种方法可以根据数字的大小自动检测长度? - Unknown

0

对于任意数字,最可靠的方法之一是使用“array”模块,如下所示:

from array import array
binArr = array('B')

while(data):
    d = data & 0xFF
    binArr.append(d)
    data >>= 8

hexStr = binArr.tostring()

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