2009-05-13 2 views
6

Когда я кормить UTF-8 кодированный XML к экземпляру ExpatParser:Настройка кодировки для парсера SAX в Python

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

Ваш код не работает в Python 2.6, но работает в версии 3.0.

Это делает работу в 2.6, по-видимому, потому что это позволяет сам анализатор, чтобы выяснить кодировку (возможно, путем считывания кодирования необязательно, указанный на первой строке файла XML, а в противном случае недобросовестный UTF-8):

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

Это работало в 2.5, тоже. –

5

Анализатор SAX в Python 2.6 должен быть способен анализировать 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, это создаст исключение, которое вы видите, потому что функция печати должна преобразовывать символы в ascii для вывода.

У вас есть 3 возможных решения:

One: Убедитесь, что ваш терминал поддерживает Юникод, а затем создать sitecustomize.py запись в вашем site-packages и установить набор символов по умолчанию в UTF-8:

импорт SYS sys.setdefaultencoding ('UTF-8')

Два: не печатает вывод на терминал (неискренний)

Три: Нормализация вывод, используя unicodedata.normalize для преобразования не-ASCII символы в кодировке ASCII эквиваленты или encode в гольцов в ASCII для вывода текста: ch.encode('ascii', 'replace'). Конечно, используя этот метод, вы не сможете правильно оценить текст.

Используя один вариант выше, ваш код работал отлично для моего в Python 2.5.

+1

Реальная проблема в исходном вопросе не связана с печатью unicode на терминал. Это связано с тем, что OP предварительно декодировал вход с помощью codecs.open, как идентифицировал Stephan202. – DanC

5

Jarret Hardie уже объяснил проблему. Но те из вас, кто кодирования для командной строки, и, кажется, не имеют «sys.setdefaultencoding» видно, быструю работу вокруг этой ошибки (или «особенность») является:

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

Надеюсь reload(sys) ничего не сломает.

Подробнее в этом старом блоге:

The Illusive setdefaultencoding

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 

Там могут быть и другие переменные, сохраняющие, но они, кажется, как самый важный.

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) 

Это позволяет разборе файла XML, который имеет не-ASCII, не-UTF8 кодировке. Например, можно разобрать расширенный файл ASCII, закодированный с LATIN1 как: (. Добавлено этим ответом напрямую обратиться названием этого вопроса, так как она имеет тенденцию к ранжировать высоко в поисковых системах) test(filename, "latin1")