当我将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”?
答案 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)
不会破坏任何其他内容。
这个旧博客的更多细节:
答案 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
可能还有其他变量需要保留,但这些变量似乎是最重要的。