在Python中设置SAX解析器的编码

6 投票
5 回答
7891 浏览
提问于 2025-04-15 11:33

当我把一个用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'呢?

5 个回答

5

Jarret Hardie已经解释了这个问题。不过,对于那些在命令行编程的人来说,如果你看不到“sys.setdefaultencoding”,那么解决这个问题的一个简单方法是:

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

希望 reload(sys) 不会导致其他问题。

更多细节可以在这篇旧博客中找到:

难以捉摸的setdefaultencoding

5

在Python 2.6中,SAX解析器应该能够正确解析utf-8格式的内容,而不会出现乱码。不过,你没有提到你使用的内容处理器,如果这个处理器试图在控制台上打印任何非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的控制台中运行时,这会产生你看到的异常,因为打印函数必须将字符转换为ASCII格式才能输出。

你有三种可能的解决方案:

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

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

第二种:不把输出打印到终端(开玩笑的说)

第三种:使用unicodedata.normalize来规范化输出,将非ASCII字符转换为ASCII等价字符,或者用encode将字符编码为ASCII格式以便输出文本:ch.encode('ascii', 'replace')。当然,使用这种方法你将无法正确评估文本。

使用上面第一种方法时,我的代码在Python 2.5中运行得很好。

5

你的代码在Python 2.6中运行失败,但在3.0中可以正常工作。

在2.6中,这段代码是可以正常工作的,可能是因为它让解析器自己去判断编码(也许是通过读取XML文件第一行上可选指定的编码,如果没有指定就默认使用utf-8):

def test(filename):
    parser = xml.sax.make_parser()
    parser.parse(open(filename))

撰写回答