【问题标题】:Alternative XML parser for ElementTree to ease UTF-8 woes?ElementTree 的替代 XML 解析器以缓解 UTF-8 问题?
【发布时间】:2010-11-11 11:09:27
【问题描述】:

我正在使用 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

【问题讨论】:

    标签: python xml utf-8 elementtree


    【解决方案1】:

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

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

    这确实适用于 ElementTree。

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

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

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

    【讨论】:

    • 不是欧洲人,我们肯定在美国。我不这样做,我保证:)
    • 你的问题是乱码:你说的文字是“canít”,它是一个小写字母I,带有一个锐音(u2019)。我定期处理足够多的未知外语,我将其解释为书面形式。请修正问题。答案是一样的;只需将 CP852 替换为 CP1252。顺便说一句,CP1252 中的 0x92 不是撇号,它是一个正确的单引号。我可能不应该对某些软件被破坏到足以弄错撇号而感到惊讶。 (不是你的错——输出那个字符串的任何软件的错。)
    • @Glenn Maynard:(1) OP 对非 ASCII 文本的复制经常出现乱码。你所看到的并不总是他们所拥有的。 the_raw_bytes.repr() 是他们的朋友,也是你的朋友。他的“撇号”是一条重要线索(2)“小写字母 I 带有急性(u2019)”:嗯?根据 Unicode 标准,U+2019 是右单引号,在 cp1252 中编码时为 0x92 (3) 据称被破坏的软件的制造者一定已经阅读了有关 U+2019 的 Unicode 标准:“这是用于撇号”。 (4)cp852?它的 0x92 -> 小写字母 L(我看不到)带有急性
    • 我必须指出,如果 Unicode 标准规定撇号的首选字符是右引号,那么 Unicode 标准是错误的。这在许多明显的方面违反了常识,我可以保证 0x27 撇号将继续保持正确的撇号表示。
    • 对不起,不清楚,但文本确实是:63 61 6E 92 74,不管它在特定编辑器中是什么样子。
    【解决方案2】:

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

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

    XML 文档可能以这样的声明开头:

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

    或者像这样: &lt;?xml version="1.0"?&gt; 或者根本没有声明......在每种情况下,解析器都将使用 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
    

    【讨论】:

      【解决方案3】:

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

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

      我也会推荐 lxml 作为 Python 中的 XML 库,但这不是这里的问题。

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 2017-01-16
          • 2023-03-25
          • 1970-01-01
          • 2012-11-07
          • 2021-02-08
          • 2013-07-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多