6

当我将 utf-8 编码的 xml 提供给 ExpatParser 实例时:

def test(filename):
    parser = xml.sax.make_parser()
    with codecs.open(filename, 'r', encoding='utf-8') as f:
        for line in f:
            parser.feed(line)

...我得到以下信息:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 72, in search_test
    parser.feed(line)
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/xml/sax/expatreader.py", line 207, in feed
    self._parser.Parse(data, isFinal)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xb4' in position 29: ordinal not in range(128)

我可能在这里遗漏了一些明显的东西。如何将解析器的编码从“ascii”更改为“utf-8”?

4

5 回答 5

5

您的代码在 Python 2.6 中失败,但在 3.0 中有效。

这在 2.6 中确实有效,大概是因为它允许解析器本身找出编码(可能通过读取 XML 文件第一行可选指定的编码,否则默认为 utf-8):

def test(filename):
    parser = xml.sax.make_parser()
    parser.parse(open(filename))
于 2009-05-13T12:22:58.547 回答
5

Python 2.6 中的 SAX 解析器应该能够解析 utf-8 而不会破坏它。尽管您省略了与解析器一起使用的 ContentHandler,但如果该内容处理程序尝试将任何非 ascii 字符打印到您的控制台,则会导致崩溃。

例如,假设我有这个 XML 文档:

<?xml version="1.0" encoding="utf-8"?>
<test>
   <name>Champs-Élysées</name>
</test>

而这个解析器:

import xml.sax

class MyHandler(xml.sax.handler.ContentHandler):

    def startElement(self, name, attrs):
        print "StartElement: %s" % name

    def endElement(self, name):
        print "EndElement: %s" % name

    def characters(self, ch):
        #print "Characters: '%s'" % ch
        pass

parser = xml.sax.make_parser()
parser.setContentHandler(MyHandler())

for line in open('text.xml', 'r'):
    parser.feed(line)

这将很好地解析,并且内容确实会保留 XML 中的重音字符。唯一的问题是def characters()我已经注释掉的那一行。在 Python 2.6 的控制台中运行,这将产生您所看到的异常,因为 print 函数必须将字符转换为 ascii 才能输出。

您有 3 种可能的解决方案:

:确保您的终端支持 unicode,然后sitecustomize.py在您的终端中创建一个条目site-packages并将默认字符集设置为 utf-8:

导入 sys sys.setdefaultencoding('utf-8')

:不要将输出打印到终端(面面相觑)

unicodedata.normalize:使用将非ascii字符转换为ascii等价物或encode将字符转换为ascii以进行文本输出的规范化输出: ch.encode('ascii', 'replace'). 当然,使用这种方法您将无法正确评估文本。

使用上面的选项一,您的代码在 Python 2.5 中工作得很好。

于 2009-05-13T13:18:29.203 回答
5

Jarret Hardie 已经解释了这个问题。但是那些正在为命令行编码,并且似乎没有“sys.setdefaultencoding”可见的人,解决这个错误(或“功能”)的快速工作是:

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

希望reload(sys)不会破坏其他任何东西。

此旧博客中的更多详细信息:

虚幻的 setdefaultencoding

于 2009-12-04T18:03:11.063 回答
3

要为 SAX 解析器设置任意文件编码,可以使用InputSource,如下所示:

def test(filename, encoding):
    parser = xml.sax.make_parser()
    with open(filename, "rb") as f:
        input_source = xml.sax.xmlreader.InputSource()
        input_source.setByteStream(f)
        input_source.setEncoding(encoding)
        parser.parse(input_source)

这允许解析具有非 ASCII、非 UTF8 编码的 XML 文件。例如,可以解析用 LATIN1 编码的扩展 ASCII 文件,例如: test(filename, "latin1")

(添加此答案以直接解决此问题的标题,因为它在搜索引擎中往往排名很高。)

于 2015-11-08T19:24:21.313 回答
0

评论 janpf 的答案(对不起,我没有足够的声誉把它放在那里),请注意 Janpf 的版本会破坏 IDLE,这需要它自己的标准输出等,这与 sys 的默认值不同。所以我建议将代码修改为:

import sys

currentStdOut = sys.stdout
currentStdIn = sys.stdin
currentStdErr = sys.stderr

reload(sys)
sys.setdefaultencoding('utf-8')

sys.stdout = currentStdOut
sys.stdin = currentStdIn
sys.stderr = currentStdErr

可能还有其他变量需要保留,但这些似乎是最重要的。

于 2012-08-20T22:27:44.757 回答