【问题标题】:Why does printing these values give different values in different OS and versions?为什么打印这些值会在不同的操作系统和版本中给出不同的值?
【发布时间】:2018-12-18 14:37:49
【问题描述】:

为什么打印这些 \x 值会在不同的操作系统和版本中给出不同的值? 示例:

print("A"*20+"\xef\xbe\xad\xde")

这在 Python3 和 2 以及不同的平台上给出了不同的输出

在微软的 Windows 中:

Python2:AAAAAAAAAAAAAAAAAAAAï¾Þ

Python3:AAAAAAAAAAAAAAAAAAAAï¾Þ

在卡利:

Python2:AAAAAAAAAAAAAAAAAAAAᆳ

Python3:AAAAAAAAAAAAAAAAAAAAï¾­Þ

更新:我想要的是确切的 Python2 输出,但使用 Python3?我尝试了很多东西(编码、解码、字节转换),但意识到 \xde 无法解码。还有其他方法可以实现我想要的吗?

【问题讨论】:

  • 我认为这是 unicode,而不是您正在打印的十六进制。
  • 这不是十六进制。
  • 所以要明确一点,你想输出AAAAAAAAAAAAAAAAAAAAᆳ,默默地忽略\xde
  • 请告诉我为什么它被否决。

标签: python python-3.x python-2.7 unicode


【解决方案1】:

这是编码的问题。

在 Latin1 或 Windows 1252 编码中,您有:

0xef -> ï (LATIN SMALL LETTER I WITH DIAERESIS)
0xbe -> ¾ (VULGAR FRACTION THREE QUARTERS)
0xad -> undefined and non printed in your examples
0xde -> Þ (LATIN CAPITAL LETTER THORN)

在 utf-8 编码中,你有:

'\xef\xbe\xad' -> u'\uffad''ᆳ'(半角韩文字母 RIEUL-SIOS) '\xde' -> 应该引发 UnicodeDecodeError...

在 Windows 中,Python2 或 Python3 都使用 Windows 1252 代码页(在您的示例中)。在 Kali 上,Python2 将字符串视为字节字符串,终端以 utf8 显示,而 Python3 假设它已经包含 unicode 字符值并直接显示。

与在 Latin1 中(以及在 Windows 1252 中,对于 0x80-0x9f 之外的所有字符)一样,字节码是 unicode 值,足以解释您的输出。

学习内容:明确字符串是否包含 unicode 或字节,并注意编码!

【讨论】:

    【解决方案2】:

    要在 Python 2 和 Python 3 上获得一致的行为,您需要明确说明您的预期输出。如果你愿意,AAAAAAAAAAAAAAAAAAAAᆳ,那么\xde 就是垃圾;如果你想要AAAAAAAAAAAAAAAAAAAAï¾Þ\xad 就是垃圾。无论哪种方式,打印您所拥有的内容的“解决方案”是显式使用 bytes 文字和 decode 以所需的编码,忽略错误。所以要获得AAAAAAAAAAAAAAAAAAAAᆳ(解释为UTF-8),你可以这样做:

    print((b"A"*20+b"\xef\xbe\xad\xde").decode('utf-8', errors='ignore'))
    

    如果要获得AAAAAAAAAAAAAAAAAAAAï¾Þ,你会这样做:

    # cp1252 can be used instead of latin-1, depending on intent; they overlap in this case
    print((b"A"*20+b"\xef\xbe\xad\xde").decode('latin-1', errors='ignore'))
    

    重要的是,请注意文字前面的b;它们在 Python 2.7 上被识别和忽略(除非 from __future__ unicode_literals 生效,在这种情况下,就像在 Python 3 中一样需要它们)和在 Python 3 上,它使文字 bytes 文字(假定没有特殊编码) ,而不是 str 文字,因此您可以使用所需的编码进行解码。无论哪种方式,您最终都会得到原始字节,然后可以使用首选编码对其进行解码,而忽略错误。

    请注意,忽略错误通常是错误的;你把数据丢在地上。 0xDEADBEEF 不能保证在任何给定的编码中产生有用的字节字符串,如果这不是你的真实数据,你可能仍然会因为想要默默地忽略不可解码的数据而冒着错误的风险。

    如果您想写入原始字节并让任何消耗stdout 的内容随心所欲地解释它们,您需要低于print 级别,因为Python 3 上的print 纯粹基于str。要在 Python 3 上写入原始字节,您可以使用 sys.stdout.buffersys.stdout 是基于文本的,sys.stdout.buffer 是它包装的底层缓冲的面向字节的流);您还需要手动添加换行符(如果需要):

    sys.stdout.buffer.write(b"A"*20+b"\xef\xbe\xad\xde\n")
    

    对比在 Python 2 上,stdout 不是编码包装器:

    sys.stdout.write(b"A"*20+b"\xef\xbe\xad\xde\n")
    

    对于可移植代码,您可以提前获取“原始标准输出”并使用它:

    # Put this at the top of your file so you don't have to constantly recheck/reacquire
    # Gets sys.stdout.buffer if it exists, sys.stdout otherwise
    bstdout = getattr(sys.stdout, 'buffer', sys.stdout)
    
    # Works on both Py2 and Py3
    bstdout.write(b"A"*20+b"\xef\xbe\xad\xde\n")
    

    【讨论】:

    • 正如你所说,它忽略了 \xde。有没有办法像 Py2 一样打印原始字节?
    • @Anutrix:不是print,而是在Py3上,sys.stdout.buffer.write(b"A"*20+b"\xef\xbe\xad\xde\n"会达到同样的效果。 sys.stdout.bufferstdout 的原始字节级文件对象。
    • 非常感谢。 sys.stdout.buffer 是我要找的。自从我更新了我的问题后,也许可以将其添加到您的答案中,以便我可以接受您的答案。
    • @Anutrix:完成。也使它成为一个跨版本的可移植示例。
    猜你喜欢
    • 2017-11-29
    • 1970-01-01
    • 2012-12-24
    • 2020-05-22
    • 1970-01-01
    • 2019-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多