如果不考虑编码,您无法将字符串转换为字节或字节转换为字符串。关于bytes 类型的全部要点是与编码无关的字节序列,而str 是Unicode code points 序列,设计没有唯一的字节表示。
因此,当您想将一种转换为另一种时,您必须明确告诉您要使用什么编码来执行此转换。转换成字节的时候,就不得不说如何将每个字符表示为一个字节序列;当你从字节转换时,你必须说明使用什么方法将这些字节映射成字符。
如果您不指定编码,则 UTF-8 是默认值,这是一个合理的默认值,因为 UTF-8 无处不在,但它也只是许多有效编码中的一个。
如果您使用原始字符串 '\xc4\xb7\x86\x17\xcd',请查看这些字符代表的 Unicode 代码点。 \xc4 例如是LATIN CAPITAL LETTER A WITH DIAERESIS,即Ä。该字符恰好在 UTF-8 中编码为0xC3 0x84,这就解释了为什么将其编码为字节时会出现这种情况。但它也有 0x00C4 的编码,例如 UTF-16。
至于如何正确地解决这个问题,让你得到想要的输出,目前还没有明确的正确答案。 Kasramvd 提到的解决方案也有些不完善。如果您了解raw_unicode_escape 编解码器in the documentation:
raw_unicode_escape
其他代码点使用 \uXXXX 和 \UXXXXXXXX 的拉丁语 1 编码。现有的反斜杠不会以任何方式转义。在 Python pickle 协议中使用。
所以这只是一个Latin-1 encoding,它有一个内置的针对它之外的字符的后备。我认为这种后备对您的目的有些有害。对于无法表示为 \xXX 序列的 Unicode 字符,这可能会有问题:
>>> chr(256).encode('raw_unicode_escape')
b'\\u0100'
因此,代码点 256 显式地位于 Latin-1 之外,这会导致 raw_unicode_escape 编码改为返回字符串 '\\u0100' 的编码字节,从而将该字符转换为 6 个字节,与原始字符几乎没有关系(因为它是一个转义序列)。
因此,如果您想在此处使用 Latin-1,我建议您明确地使用它,而无需从 raw_unicode_escape 回退转义序列。这只会在尝试转换 Latin-1 区域之外的代码点时导致异常:
>>> '\xc4\xb7\x86\x17\xcd'.encode('latin1')
b'\xc4\xb7\x86\x17\xcd'
>>> chr(256).encode('latin1')
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
chr(256).encode('latin1')
UnicodeEncodeError: 'latin-1' codec can't encode character '\u0100' in position 0: ordinal not in range(256)
当然,Latin-1 区域之外的代码点是否会给您带来问题取决于该字符串的实际来源。但是,如果您可以保证输入将仅包含有效的 Latin-1 字符,那么您很可能一开始就不需要使用字符串。由于您实际上正在处理某种字节,因此您应该首先查看是否不能简单地将这些值作为字节检索。这样一来,您就不会在那里引入 两个 级别的编码,您可能会通过误解输入来破坏数据。