出现了以下问题,因为我试图将bytes
字符串用作字典键,而我理解为相等的字节值并未被视为相等。
为什么下面的python代码没有比较相同 - 不是这两个相同二进制数据的等价表示(为避免endianess而故意选择的例子)?
b'0b11111111' == b'0xff'
我知道以下评估为真,证明了等价:
int(b'0b11111111', 2) == int(b'0xff', 16)
但是为什么python强迫我知道表示?它与endian-ness有关吗?是否有一些简单的方法来强制这些比较等效,而不是将它们全部转换为例如十六进制文字?任何人都可以建议一种透明而清晰的方法,以(某种程度上)平台无关的方式在所有表示之间移动(或者我要求的太多)?
编辑:
鉴于下面的评论,假设我想实际使用b'0b11111111'
形式的8位索引字典,那么为什么python将它扩展为10个字节,我该如何防止它?
这是一个较小的大型树数据结构,将我的索引扩展80倍似乎是对内存的巨大浪费。
答案 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_uint8
和c_int64
整数)。
要转换回来:
>>> ctypes.c_ubyte.from_buffer_copy(b'\xff').value
255
小心溢出:
>>> ctypes.c_ubyte(256)
c_ubyte(0)