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

时间:2009-05-13 12:09:12

标签: python unicode sax

当我将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 个答案:

答案 0 :(得分:5)

您的代码在Python 2.6中失败,但在3.0中运行。

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

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

答案 1 :(得分: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.setdefaultencoding函数( 'UTF-8')

两个:不要将输出打印到终端(诙谐)

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

使用上面的选项1,您的代码在我的Python 2.5中运行得很好。

答案 2 :(得分:5)

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

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

希望reload(sys)不会破坏任何其他内容。

这个旧博客的更多细节:

The Illusive setdefaultencoding

答案 3 :(得分: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")

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

答案 4 :(得分:0)

评论janpf的答案(对不起,我没有足够的声誉把它放在那里),请注意,Janpf的版本将破坏IDLE,这需要自己的stdout等,这与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

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