Установка кодировки для синтаксического анализатора в Python - PullRequest
6 голосов
/ 13 мая 2009

Когда я передаю кодированный в кодировке utf-8 экземпляр 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 ]

5 голосов
/ 13 мая 2009

Анализатор 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 : убедитесь, что ваш терминал поддерживает Unicode, затем создайте запись sitecustomize.py в вашем site-packages и установите для набора символов по умолчанию значение utf-8:

import sys sys.setdefaultencoding ( 'UTF-8')

Два : Не печатать вывод на терминал (насмешливо)

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

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

5 голосов
/ 04 декабря 2009

Джаррет Харди уже объяснил проблему. Но те из вас, кто пишет код для командной строки и, кажется, не видит видимое «sys.setdefaultencoding», быстро обходит эту ошибку (или «функцию»):

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

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

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

Призрачный набор defaultencoding

5 голосов
/ 13 мая 2009

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

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

def test(filename):
    parser = xml.sax.make_parser()
    parser.parse(open(filename))
3 голосов
/ 08 ноября 2015

Чтобы установить произвольную кодировку файла для синтаксического анализатора 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")

(Добавлен этот ответ, чтобы напрямую обратиться к названию этого вопроса, так как он имеет высокий рейтинг в поисковых системах.)

0 голосов
/ 21 августа 2012

Комментируя ответ janpf (извините, у меня недостаточно репутации, чтобы поместить его туда), обратите внимание, что версия Janpf сломает IDLE, для которой требуется собственный стандартный вывод и т. Д., Который отличается от значения по умолчанию 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

Могут быть и другие переменные для сохранения, но они кажутся наиболее важными.

...