2009-05-13 4 views
6

Wenn ich eine utf-8 kodierten xml zu einer ExpatParser Instanz füttern:die Codierung für SAX-Parser in Python Einstellung

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) 

... ich folgendes:

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) 

Ich bin Vermutlich fehlt hier etwas offensichtlich. Wie ändere ich die Codierung des Parsers von 'ASCII' in 'UTF-8'?

Antwort

5

Ihr Code schlägt in Python 2.6 fehl, funktioniert aber in 3.0.

Dies funktioniert in 2.6, vermutlich, weil es der Parser selbst ermöglicht die Codierung, um herauszufinden, (vielleicht von der ersten Zeile der XML-Datei, die Kodierung optional angegeben zu lesen, und sonst zu utf-8 angenommen wird):

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

Das funktionierte in 2.5 auch. –

5

Der SAX-Parser in Python 2.6 sollte in der Lage sein, utf-8 zu analysieren, ohne ihn zu verfälschen. Obwohl Sie den ContentHandler, den Sie mit dem Parser verwenden, weggelassen haben, führt dieser Content-Handler zu einem Absturz, wenn er versucht, nicht-ASCII-Zeichen auf Ihrer Konsole zu drucken.

Zum Beispiel, sagen, dass ich diese XML-doc:

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

Und diese Parsing Apparat:

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) 

Dies wird analysieren ganz gut, und der Inhalt werden die Zeichen mit Akzent in der Tat erhalten XML. Das einzige Problem ist die Zeile in def characters(), die ich auskommentiert habe. In der Konsole in Python 2.6 ausgeführt, wird dies die Ausnahme erzeugen, die Sie sehen, da die Druckfunktion die Zeichen in ascii für die Ausgabe konvertieren muss.

Sie haben 3 mögliche Lösungen:

One: Stellen Sie sicher, dass Ihr Terminal Unicode unterstützt, dann erstellen Sie einen sitecustomize.py Eintrag in Ihrem site-packages und stellen Sie die Standardzeichen auf utf-8 gesetzt:

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

Zwei: drucken Sie nicht die Ausgabe an das Endgerät (tongue-in-Wange)

Drei: Normalisieren des Ausgangs unicodedata.normalize unter Verwendung von nicht-ASCII-Zeichen in ASCII-Äquivalente zu konvertieren oder encode die Zeichen für die Textausgabe ASCII: ch.encode('ascii', 'replace'). Mit dieser Methode können Sie den Text natürlich nicht richtig auswerten.

Mit Option eins oben, funktionierte Ihr Code für meine in Python 2.5.

+1

Das eigentliche Problem in der ursprünglichen Frage hat nichts mit dem Drucken von Unicode an das Terminal zu tun. Das liegt daran, dass das OP die Eingabe mit codecs.open vorverschlüsselt hat, wie Stephan202 festgestellt hat. – DanC

5

Jarret Hardie hat das Problem bereits erläutert. Aber diejenigen, die Codierung für die Kommandozeile sind, und scheinen nicht das zu haben „sys.setdefaultencoding“ sichtbar, die schnelle Arbeit, um diesen Fehler (oder „Feature“) ist:

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

Hoffentlich reload(sys) wird nichts anderes brechen.

Mehr Details in diesem alten Blog:

The Illusive setdefaultencoding

0

auf janpf Antwort Kommentar (sorry, ich habe nicht genug Ruf, den sie dort setzen), beachten Sie, dass Janpf Version IDLE brechen wird, die seine erfordert eigene stdout usw., die sich von sys Standard unterscheidet. Also würde ich den Code zu modifizieren vorschlagen, wie etwas zu sein:

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 

Es können auch andere Variablen zu erhalten, aber diese scheinen, wie die wichtigsten.

3

Um eine beliebige Datei-Codierung für einen SAX-Parser eingestellt, kann man InputSource wie folgt verwenden:

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) 

Dies ermöglicht eine XML-Datei Parsen, die eine Nicht-ASCII, nicht-UTF8-Codierung hat. Zum Beispiel kann man eine erweiterte ASCII-Datei mit LATIN1 wie codiert analysieren: (. Hinzugefügt diese Antwort direkt den Titel dieser Frage zu beantworten, da es hoch in Suchmaschinen Rang neigt) test(filename, "latin1")