ElementTree的替代XML解析器可以缓解UTF-8问题?

时间:2009-07-16 17:32:17

标签: python xml utf-8 elementtree

我正在使用elementtree.parse()函数解析一些XML。它工作,除了一些utf-8字符(128字节以上的单字节字符)。我看到默认的解析器是XMLTreeBuilder,它基于expat。

我可以使用的替代解析器可能不那么严格并且允许使用utf-8字符吗?

这是我使用默认解析器得到的错误:

ExpatError: not well-formed (invalid token): line 311, column 190

导致此字符的字符是单字节x92(十六进制)。我不确定这甚至是一个有效的utf-8字符。但处理它会很好,因为大多数文本编辑器将其显示为:í

编辑:角色的上下文是:canít,我认为它应该是一个花哨的撇号,但在十六进制编辑器中,相同的序列是:63 61 6E 92 74 < / p>

4 个答案:

答案 0 :(得分:15)

我将从问题开始:“我可以使用的替代解析器可能不那么严格并允许使用utf-8字符吗?”

所有XML解析器都将接受以UTF-8编码的数据。实际上,UTF-8是默认编码。

XML文档可以从这样的声明开始:

`<?xml version="1.0" encoding="UTF-8"?>`

或者像这样:     <?xml version="1.0"?> 或根本没有声明...在每种情况下,解析器将使用UTF-8解码文档。

但是你的数据不是用UTF-8编码的......它可能是Windows-1252,也就是cp1252。

如果编码不是UTF-8,则创建者应该包含声明(或者收件人可以添加一个声明),或者收件人可以将数据转码为UTF-8。以下展示了哪些有效,哪些无效:

>>> import xml.etree.ElementTree as ET
>>> from StringIO import StringIO as sio

>>> raw_text = '<root>can\x92t</root>' # text encoded in cp1252, no XML declaration

>>> t = ET.parse(sio(raw_text))
[tracebacks omitted]
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 9
# parser is expecting UTF-8

>>> t = ET.parse(sio('<?xml version="1.0" encoding="UTF-8"?>' + raw_text))
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 47
# parser is expecting UTF-8 again

>>> t = ET.parse(sio('<?xml version="1.0" encoding="cp1252"?>' + raw_text))
>>> t.getroot().text
u'can\u2019t'
# parser was told to expect cp1252; it works

>>> import unicodedata
>>> unicodedata.name(u'\u2019')
'RIGHT SINGLE QUOTATION MARK'
# not quite an apostrophe, but better than an exception

>>> fixed_text = raw_text.decode('cp1252').encode('utf8')
# alternative: we transcode the data to UTF-8

>>> t = ET.parse(sio(fixed_text))
>>> t.getroot().text
u'can\u2019t'
# UTF-8 is the default; no declaration needed

答案 1 :(得分:4)

看起来你有CP1252文字。如果是,则应在文件顶部指定,例如:

<?xml version="1.0" encoding="CP1252" ?>

这适用于ElementTree。

如果您自己创建这些文件,请不要使用此编码进行编写。将它们保存为UTF-8并尽力帮助杀死过时的文本编码。

如果您正在接收没有编码规范的CP1252数据,并且您确定它始终是CP1252,您可以在将其发送到解析器之前将其转换为UTF-8:

s.decode("CP1252").encode("UTF-8")

答案 2 :(得分:1)

字节0x92永远不会作为UTF-8字符的第一个字节有效。但是,它可以作为后续字节有效。有关有效字节序列的表,请参见this UTF-8 guide

你能告诉我们0x92周围的字节是什么吗? XML声明是否包含字符编码?

答案 3 :(得分:1)

阿。显然,这是“不可能”,事实上,在许多Windows代码页中,0x92是一个撇号。您的编辑器假设它是一个Mac文件。 ;)

如果是一次性的,修复文件是正确的做法。但几乎总是当你需要导入其他人的XML时,有很多东西根本不同意所述的编码。我发现最好的解决方案是使用错误设置'xmlcharrefreplace'进行解码,并且在严重的情况下执行您自己的自定义字符替换,以修复该特定客户的最常见问题。

我还会在Python中推荐lxml作为XML库,但这不是问题所在。