9

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

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

这是我在使用默认解析器时遇到的错误:

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

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

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

4

4 回答 4

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 aka 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
于 2009-07-17T04:43:54.087 回答
4

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

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

这确实适用于 ElementTree。

如果您自己创建这些文件,请不要以这种编码方式编写它们。将它们保存为 UTF-8 并尽自己的一份力量帮助消除过时的文本编码。

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

s.decode("CP1252").encode("UTF-8")
于 2009-07-16T18:49:48.710 回答
1

字节 0x92 永远不会作为UTF-8 字符的第一个字节有效。但是,它可以作为后续字节有效。请参阅此 UTF-8 指南以获取有效字节序列表。

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

于 2009-07-16T17:41:49.533 回答
1

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

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

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

于 2009-07-16T18:53:36.737 回答