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

在Python中设置sax解析器的编码,python,unicode,sax,Python,Unicode,Sax,当我将utf-8编码的xml提供给解析器实例时: 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 "<

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

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)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“test.py”,第72行,在搜索测试中
提要(行)
文件“/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/xml/sax/expatreader.py”,第207行,在提要中
self.\u parser.Parse(数据,isFinal)
UnicodeEncodeError:“ascii”编解码器无法对位置29中的字符u'\xb4'进行编码:序号不在范围内(128)

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

您的代码在Python 2.6中失败,但在3.0中可以工作

这在2.6中确实起作用,可能是因为它允许解析器自己找出编码(可能是通过读取XML文件第一行上可选指定的编码,或者默认为utf-8):


Python2.6中的SAX解析器应该能够解析utf-8而不会损坏它。虽然您省略了解析器使用的ContentHandler,但如果该内容处理程序试图将任何非ascii字符打印到控制台,则会导致崩溃

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

<?xml version="1.0" encoding="utf-8"?>
<test>
   <name>Champs-Élysées</name>
</test>
这将很好地解析,并且内容将确实保留XML中的重音字符。唯一的问题是我注释掉的
def characters()
中的那一行。在Python2.6的控制台中运行,这将产生您看到的异常,因为print函数必须将字符转换为ascii进行输出

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

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

def test(filename):
    parser = xml.sax.make_parser()
    parser.parse(open(filename))
导入系统 sys.setdefaultencoding('utf-8')

Two:不要将输出打印到终端(开玩笑)

Three:使用
Unicode数据对输出进行规格化。规格化
将非ascii字符转换为ascii等效字符,或
将字符编码为ascii进行文本输出:
ch.encode('ascii',replace')
。当然,使用此方法无法正确评估文本


使用上面的选项一,您的代码在Python2.5中的my中运行良好。

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

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

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


在评论janpf的回答时(对不起,我没有足够的声誉将其放在那里),请注意janpf的版本将中断空闲,这需要它自己的标准输出等。这与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

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

要为SAX解析器设置任意文件编码,可以使用以下方法:

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文件。例如,可以解析用拉丁语编码的扩展ASCII文件,如:
test(文件名,“拉丁语”)


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

原始问题中的实际问题与将unicode打印到终端无关。正如Stephan202所指出的,这是由于OP使用codecs.open对输入进行了预解码。
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
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)