Python字节文字的比较

时间:2014-07-19 16:54:49

标签: python comparison byte endianness base

出现了以下问题,因为我试图将bytes字符串用作字典键,而我理解为相等的字节值并未被视为相等。

为什么下面的python代码没有比较相同 - 不是这两个相同二进制数据的等价表示(为避免endianess而故意选择的例子)?

b'0b11111111' == b'0xff'

我知道以下评估为真,证明了等价:

int(b'0b11111111', 2) == int(b'0xff', 16)

但是为什么python强迫我知道表示?它与endian-ness有关吗?是否有一些简单的方法来强制这些比较等效,而不是将它们全部转换为例如十六进制文字?任何人都可以建议一种透明而清晰的方法,以(某种程度上)平台无关的方式在所有表示之间移动(或者我要求的太多)?

编辑:

鉴于下面的评论,假设我想实际使用b'0b11111111'形式的8位索引字典,那么为什么python将它扩展为10个字节,我该如何防止它?

这是一个较小的大型树数据结构,将我的索引扩展80倍似乎是对内存的巨大浪费。

3 个答案:

答案 0 :(得分:8)

字节可以代表任意数量的内容。 Python不能也不会猜测你的字节可能编码的内容。

例如,int(b'0b11111111', 34) 也是一个有效的解释,但该解释不等于十六进制FF。

事实上,解释的数量是无穷无尽的。字节可以表示一系列ASCII码点,图像颜色或音符。

在明确应用解释之前,bytes对象包含0-255范围内值序列的 ,如果可以表示为可打印文本,则这些字节的文本表示使用ASCII:

>>> list(bytes(b'0b11111111'))
[48, 98, 49, 49, 49, 49, 49, 49, 49, 49]
>>> list(bytes(b'0xff'))
[48, 120, 102, 102]

那些字节序列不相等。

如果要将这些序列显式解释为整数文字,请使用ast.literal_eval()来解释已解码的文本值;在比较之前总是先规范化:

>>> import ast
>>> ast.literal_eval(b'0b11111111'.decode('utf8'))
255
>>> ast.literal_eval(b'0xff'.decode('utf8'))
255

答案 1 :(得分:5)

b'0b11111111'由10个字节组成:

In [44]: list(b'0b11111111')
Out[44]: ['0', 'b', '1', '1', '1', '1', '1', '1', '1', '1']

b'0xff'由4个字节组成:

In [45]: list(b'0xff')
Out[45]: ['0', 'x', 'f', 'f']

显然,它们不是同一个物体。

Python值显性。 (显式优于隐式。)假设 b'0b11111111'必然是整数的二进制表示。它只是一串字节。你必须明确说明如何解释它。

答案 2 :(得分:0)

似乎您要尝试获取的字节字符串表示值0b11111111(或255)。这不是b'0b11111111'所做的–实际上代表一个表示字符(Unicode)字符串'0b11111111'的字节字符串。

您想要的内容将写为b'\xff'。您可以检查它是否实际上是一个字节:len(b'\xff') == 1

要将Python int转换为二进制表示形式,可以使用ctypes库。您需要选择一种C整数类型,例如:

>>> bytes(ctypes.c_ubyte(255))
b'\xff'

>>> bytes(ctypes.c_ubyte(0xff))
b'\xff'

>>> bytes(ctypes.c_long(255))
b'\xff\x00\x00\x00\x00\x00\x00\x00'

注意:您可以使用别名c_ubyte(即8位无符号C整数)和c_long(64位有符号C)来代替c_uint8c_int64整数)。

要转换回来:

>>> ctypes.c_ubyte.from_buffer_copy(b'\xff').value
255

小心溢出:

>>> ctypes.c_ubyte(256)
c_ubyte(0)