猿问

启用从 python 3.7.3 中的字节正确解码/编码

我正在为此苦苦挣扎:

b'"\xc2\xb7\xed\xa0\x81\xed\xb1\x96\xed\xa0\x81\xed\xb1\xb1\xed\xa0\x81\xed\xb1\x9d\xed\xa0\x81\xed\xb1\xbe\xed\xa0\x81\xed\xb1\xaf \xed\xa0\x81\xed\xb1\xa9\xed\xa0\x81\xed\xb1\xa4\xed\xa0\x81\xed\xb1\x93\xed\xa0\x81\xed\xb1\xa9\xed\xa0\x81\xed\xb1\x9a\xed\xa0\x81\xed\xb1\xa7\xed\xa0\x81\xed\xb1\x91"@en'

它来自(dbpedia 3.5.1 ( http://dbpedia.org/page/Shavian_alphabet )) 的 HDT 压缩版本 ( https://github.com/rdfhdt/hdt-cpp ) 的二进制格式并且很好本网站以 utf8 解码 ( https://mothereff.in/utf-8 )

意思是:“·𐑖𐑱𐑝𐑾𐑯 𐑩𐑤𐑓𐑩𐑚𐑧𐑑”@zh

但是在 python 3.7.3 中我遇到了众所周知的错误:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xed in position 3: invalid continuation byte当试图mystring.decode('utf8')

如果我试图做相反的事情: '"·𐑖𐑱𐑝𐑾𐑯 𐑩𐑤𐑓𐑩𐑚𐑧𐑑"@en'.encode('utf8)我得到以下表示:b'"\xf0\x90\x91\x96\xf0\x90\x91\xb1\xf0\x90\x91\x9d\xf0\x90\x91\xbe\xf0\x90\x91\xaf \xf0\x90\x91\xa8\xf0\x90\x91\xa4\xf0\x90\x91\x93\xf0\x90\x91\xa9\xf0\x90\x91\x9a\xf0\x90\x91\xa7\xf0\x90\x91\x91"@en'这不是完全相同的字符串,但随后被repr.decode('utf8')正确解码为相同的东西......

有人可以帮我理解为什么解码第一个字节字符串不起作用吗?由于错误,我知道第一个字节字符串不是有效的 UTF-8 字符串。但是,为什么它被我链接的网站很好地解码而不能由python完成?先感谢您!


最终编辑 在接受了答案后,我对此做了一些额外的研究,发现这个字符串是使用 CESU-8 编解码器编码的。今天显然已弃用。但有些人仍在使用它......所以,我找到了一个包,它编写了一个可以解码这个字符串的 utf-8 编解码器的变体。我想它会帮助很多和我有同样问题的人。Python 库:https ://github.com/LuminosoInsight/python-ftfy 添加的编解码器是“utf-8-variants”。我希望这将帮助与我有相同需求的人。


湖上湖
浏览 175回答 1
1回答

慕森王

似乎 Python 不想接受某些字节序列作为有效的 UTF-8,而某些网站(https://mothereff.in/utf-8)接受它。其中一个肯定是错的,对吧?让我们来看看。b'\xc2\xb7'Python 接受前两个字节 ( )。Python 不喜欢的第一件事是:\xed\xa0\x81\xed\xb1\x96,在那个网站上被解释为 𐑖。让我们看一下\xed\xa0\x81\xed\xb1\x96二进制格式:111011011010000010000001111011011011000110010110RFC3629说 UTF-8 被解释为:&nbsp; &nbsp;Char. number range&nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; UTF-8 octet sequence&nbsp; &nbsp; &nbsp; (hexadecimal)&nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (binary)&nbsp; &nbsp;--------------------+---------------------------------------------&nbsp; &nbsp;0000 0000-0000 007F | 0xxxxxxx&nbsp; &nbsp;0000 0080-0000 07FF | 110xxxxx 10xxxxxx&nbsp; &nbsp;0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx&nbsp; &nbsp;0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx因此,有两个三字节字符:1110 1101 10 100000 10 000001 ⇒ 1101100000000001,或 D8011110 1101 10 110001 10 010110 ⇒ 1101110001010110,或 DC56性格D801是高级代理之一,DC56也是低级代理之一。您可以在此处查看如何组合代理:代理对表示代码点 0x10000 + (H − 0xD800) × 0x400(L − 0xDC00) 其中 H 和 L 分别是高和低代理的数值。如果你把它们结合起来,你会得到:0x10000 + (0xD801 - 0xD800) * 0x400 + (0xDC56 - 0xDC00) = 0x10456,也就是𐑖然而,高和低代理是为不适合 16 位的字符的 UTF-16 表示而设计的,这就是unicode.org关于在 UTF-8 中使用这种代理对的说法:问:如何将 UTF-16 代理对转换为<D800 DC00>UTF-8?作为一个 4 字节序列还是作为两个单独的 3 字节序列?答:UTF-8 的定义要求补充字符(在 UTF-16 中使用代理对的那些)使用单个 4 字节序列进行编码。然而,在旧软件中生成 3 字节序列对的做法很普遍,尤其是在引入 UTF-16 之前或在特定约束下与 UTF-16 环境互操作的软件。这种编码不符合定义的 UTF-8。有关此类非 UTF-8 数据格式的正式描述,请参见UTR #26:UTF-16:8 位 (CESU) 的兼容性编码方案。使用 CESU-8 时,由于格式的相似性,必须非常小心不要将数据意外地视为 UTF-8。[自动对焦]这里的关键点是“这样的编码不符合定义的 UTF-8”。所以,你的输入实际上是一个无效的 UTF-8 序列,Python 拒绝了它。要回答这个问题:https://mothereff.in/utf-8忽略了 unicode.org 将其视为无效的指令。Python 将其视为无效。如果您想对其进行解码,即使它无效,您也可以编写一个函数来完成我手动执行的操作。
随时随地看视频慕课网APP

相关分类

Python
我要回答