【问题标题】:Python Comparison of byte literals字节文字的Python比较
【发布时间】:2014-07-19 16:54:48
【问题描述】:

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

为什么以下 python 代码比较不相等 - 这两个等效表示不是相同的二进制数据(故意选择避免字节序的示例)?

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

我知道以下计算结果为真,证明了等价性:

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

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

编辑:

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

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

【问题讨论】:

    标签: python comparison byte endianness base


    【解决方案1】:

    字节可以表示任意数量的事物。 Python 不能也不会猜测你的字节可能编码什么。

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

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

    在您明确应用解释之前,字节对象由 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
    

    【讨论】:

    • 但是0b 不是表示bytes 文字旨在成为二进制表示,而不管您如何解释它?
    • @MatthewHemke:这只是意味着您有一个字节值 48,后跟一个字节值 98。这些 发生 可以解释为 ASCII 字母 0b
    • 如果是这种情况,我该怎么做才能让字节字符串正好是我的意思的 1 个字节?
    【解决方案2】:

    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' 一定是整数的二进制表示。它只是一串字节。必须明确说明您选择如何解释它。

    【讨论】:

    • 实际的字节串是的,但是它们代表的数据是一样的吧?还是字节字符串本身在被例如解释时变成了值。 int('', base) 评估。
    • 是的,字节串和整数值不一样。 int 函数将字节转换为 int(并且必须指定基数)。
    【解决方案3】:

    看来您试图做的是获取一个表示值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_uint8(即 8 位无符号 C 整数)和 c_int64(64 位有符号 C 整数)来代替 c_ubytec_long

    转换回来:

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

    小心溢出:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多