【问题标题】:Why does Python automatically encode hex in strings as UTF-8?为什么 Python 会自动将字符串中的十六进制编码为 UTF-8?
【发布时间】:2018-01-13 02:35:25
【问题描述】:

我一直在使用 python 进行 ascii 到二进制的翻译,并且一直遇到解析结果的问题。最后我想看看 Python 命令生成了什么。

输出中似乎插入了胭脂0xc2(例如):

$ python -c 'print("\x80")' | xxd
00000000: c280 0a                                  ...

无论在何处使用此类字节,都会发生这种情况:

$ python -c 'print("Test\x80Test2\x81")' | xxd
00000000: 5465 7374 c280 5465 7374 32c2 810a       Test..Test2...

凭直觉,我查了一下UTF-8,果然,U+0080 被编码为0xc2 0x80。显然,Python 冒昧地假设\x80实际上是指U+0080 的编码。有没有办法改变这种默认行为或以其他方式明确指示我包含单字节 0x80 而不是 UTF 编码的意图?

Python 3.6.2

【问题讨论】:

标签: python python-3.x unicode


【解决方案1】:

Python 3 正确地将字符插入到 str 中,这是字符串,而不是字节序列。

UTF8 是默认编码。如果您需要插入一个字节,则需要将该字符表示为一个字节的不同编码。

$ PYTHONIOENCODING=iso-8859-1 python3 -c 'print("\x80")' | xxd
00000000: 800a

Python 编码

如果在运行解释器之前设置,它会覆盖用于 stdin/stdout/stderr 的编码,在语法中 编码名称:错误处理程序。 encodingname 和 :errorhandler 部分是可选的,与 str.encode() 中的含义相同。

【讨论】:

  • 更改字符串的全局编码是对局部问题的(相当不雅的)全局解决方案。如果 OP 正在构建字节序列而不是字符序列,他应该使用 byte 对象而不是 str 对象(或者,使用 encode 方法将相关的 str 对象编码为 Latin1)。
  • @MatteoItalia 是的,但我不明白这种情况是“本地问题”。 OP 要求更改默认值,并提到 ascii 到二进制的翻译是他使用该程序的目的。
  • 很公平,虽然 OP 真的 没有进行 ascii 到二进制的转换,因为 b'\x80' 不是合法的 ASCII。
  • @PM2Ring,这样的吊坠。当然,\x80 不是合法的可打印 ASCII,但很清楚我的意图是什么。也许 hex to binary 是更好的描述。
  • @sherrellbc 也许这有点迂腐,但是当你用字符编码做事情时,迂腐是值得的。 ;)
【解决方案2】:

如果您想在 Python 3 中输出原始字节,则不应使用 print 函数,因为它用于以默认编码输出文本。相反,您可以使用sys.stdout.buffer.write

ASCII 是 7 位编码,所以如果你所谓的 ASCII 包含像 b'\x80' 这样的字符,它就不是合法的 ASCII。也许您的数据实际上是使用 iso-8859-1(又名 latin-1)编码的,或者它可能是密切相关的 Windows 变体 cp1252。要正确执行此类操作,您需要确定用于创建数据的实际编码。

如果你想输出"Test\x80Test2\x81" 并让十六进制转储看起来像这样:

00000000  54 65 73 74 80 54 65 73  74 32 81                 |Test.Test2.|

你可以的

import sys
s = "Test\x80Test2\x81"
sys.stdout.buffer.write(s.encode('latin1'))

这是可行的,因为 Latin-1 是 Unicode 的一个子集。这是一个快速演示:

import binascii

a = ''.join([chr(i) for i in range(256)])
b = a.encode('latin1')
print(binascii.hexlify(b))

输出

b'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'

但是,如果您实际上是在处理二进制数据,那么您首先不应该将其存储在文本字符串中,您应该使用bytes,或者可能是bytearray。从我之前的示例中生成 b 字节字符串的合理方法是执行

b = bytes(range(256))

如果你有一个像b"Test\x80Test2\x81" 这样的bytes 对象,你可以使用

将这些字节转储到标准输出
sys.stdout.buffer.write(b"Test\x80Test2\x81")

【讨论】:

  • 感谢您的示例。没有数据存储。我的用例是生成一串 ASCII 和不可打印的字符串,然后将其直接通过管道传输到被测程序的标准输入。
  • @sherrellbc 不用担心。在这种情况下,您绝对不想为此使用文本字符串,请使用 bytesbytearrays 完成所有操作。
  • @sherrellbc 如果您将此二进制数据传输到的程序是 Python 3 程序,那么您应该让它从 sys.stdin.buffer 读取数据,不要通过调用 @ 来读取它987654340@,因为它需要文本字符串。
猜你喜欢
  • 1970-01-01
  • 2020-10-26
  • 1970-01-01
  • 2020-01-18
  • 2014-08-07
  • 2015-04-28
  • 2013-05-06
  • 1970-01-01
相关资源
最近更新 更多