在Python中设置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 个回答
Jarret Hardie已经解释了这个问题。不过,对于那些在命令行编程的人来说,如果你看不到“sys.setdefaultencoding”,那么解决这个问题的一个简单方法是:
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
希望 reload(sys)
不会导致其他问题。
更多细节可以在这篇旧博客中找到:
在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中运行得很好。
你的代码在Python 2.6中运行失败,但在3.0中可以正常工作。
在2.6中,这段代码是可以正常工作的,可能是因为它让解析器自己去判断编码(也许是通过读取XML文件第一行上可选指定的编码,如果没有指定就默认使用utf-8):
def test(filename):
parser = xml.sax.make_parser()
parser.parse(open(filename))