Pergunta

Quando alimentar uma utf-8 xml codificado para uma instância 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)

... eu recebo o seguinte:

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)

Eu sou provavelmente faltando algo óbvio aqui. Como faço para alterar a codificação do analisador de 'ascii' para 'utf-8'?

Foi útil?

Solução

O código de falha em Python 2.6, mas funciona em 3.0.

Isto funciona em 2,6, presumivelmente porque permite que o próprio analisador para descobrir a codificação (talvez lendo a codificação opcionalmente especificado na primeira linha do arquivo XML, e caso contrário assumem a utf-8):

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

Outras dicas

Jarret Hardie já explicou a questão. Mas aqueles de vocês que estão codificando para a linha de comando, e não parecem ter o "sys.setdefaultencoding", o trabalho rápido visível em torno este bug (ou "recurso") é:

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

Esperamos reload(sys) não vai quebrar qualquer outra coisa.

Mais detalhes neste blog idade:

O Illusive setdefaultencoding

O analisador SAX em Python 2.6 deve ser capaz de analisar utf-8, sem deturpação ele. Embora você tenha deixado de fora o ContentHandler você está usando com o analisador, se que tenta manipulador de conteúdo para imprimir quaisquer caracteres não-ASCII para o seu console, que irá causar um acidente.

Por exemplo, digamos que eu tenho esse documento XML:

<?xml version="1.0" encoding="utf-8"?>
<test>
   <name>Champs-Élysées</name>
</test>

E este aparelho de análise:

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)

Esta irá analisar muito bem, e o conteúdo vai realmente preservar os caracteres acentuados no XML. O único problema é que a linha em def characters() que tenho comentado. Em execução no console em Python 2.6, este irá produzir a exceção que você está vendo porque a função de impressão deve converter os caracteres para ascii para a saída.

Você tem 3 soluções possíveis:

Um : Certifique-se de seus suportes terminais Unicode, em seguida, criar uma entrada sitecustomize.py em sua site-packages e definir o conjunto de caracteres padrão para utf-8:

sys importação sys.setdefaultencoding ( 'utf-8')

Dois : Não imprima a saída para o terminal (tongue-in-cheek)

Três : Normalize a saída usando unicodedata.normalize para converter caracteres não-ascii para equivalentes ASCII, ou encode os caracteres para ascii para saída de texto: ch.encode('ascii', 'replace'). Claro, usando esse método, você não será capaz de avaliar corretamente o texto.

Usando opção citada acima, o código funcionou muito bem para o meu em Python 2.5.

Para configurar uma codificação de arquivo arbitrário para um analisador SAX, pode-se usar InputSource da seguinte forma:

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)

Isso permite analisar um arquivo XML que tem um não-ASCII, não-UTF8 codificação. Por exemplo, pode-se analisar um arquivo ASCII estendido codificado com LATIN1 como: test(filename, "latin1")

(Adicionado esta resposta para abordar diretamente o título desta questão, pois ele tende a classificar altamente nos motores de busca.)

Ao comentar sobre a resposta de janpf (desculpe, eu não tenho reputação suficiente para colocá-lo lá), nota que a versão de Janpf vai quebrar IDLE que requer sua própria stdout etc. que é diferente do padrão das sys. Então, eu sugiro modificar o código para ser algo como:

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

Pode haver outras variáveis ??para preservar, mas estes parecem ser os mais importantes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top