xml und UnicodeEncodeError

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Pascal
User
Beiträge: 271
Registriert: Samstag 4. April 2009, 22:18

Hallo,

ich unternehm grad erste Gehversuche mit xml.
Ich will für ein Vokabelabfrageprogramm die Dateien in xml-Format speichern.

Aussehen soll die Datei so.

Code: Alles auswählen

- <woerterbuch>
- <vokabel>
  <deutsch>A</deutsch> 
  <griechisch>Α</griechisch> 
  <status>0</status> 
  <datum>2.1.2012</datum> 
  </vokabel>
[...]
Erzeugt wird die datei aus einem Dictonary der Form {deutsch: [griechisch, status, letzte abfrage]}

Der Code zum Erzeugen:

Code: Alles auswählen

import xml.dom.minidom as dom

def _erstelle_vokabel(deutsch, griechisch, status, datum):
    ''' '''
    tag_vokabel = dom.Element('vokabel')
    tag_namen = ['deutsch', 'griechisch', 'status', 'datum']
    tag_werte = [deutsch, griechisch, status, datum]
    tags = [dom.Element(t) for t in tag_namen]
   
    for name, wert, tag in zip(tag_namen, tag_werte, tags):
        text = dom.Text() 
        text.data = str(wert) 
        tag.appendChild(text)
        tag_vokabel.appendChild(tag) 

    return tag_vokabel



def schreibe_woerterbuch(d, dateiname): 
    
    tag_woerterbuch = dom.Element('woerterbuch') 
    
    for schluessel, wert in d.iteritems(): 
        tag_vokabel = _erstelle_vokabel(schluessel, *wert) 
        tag_woerterbuch.appendChild(tag_vokabel)

    baum = dom.Document()
    baum.appendChild(tag_woerterbuch)

    f = open(dateiname, "w") 
    baum.writexml(f, "", "\t", "\n") 
    f.close()
Dies funktioniert, solange ich keine Sonderzeichen verwenden. (Bei griechisch jedoch unumgänglich...)

Ich bekomme als Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "<pyshell#102>", line 1, in <module>
    schreibe_woerterbuch(d, 'testdaten.xml')
  File "C:\Python26\PyProgramme\xml_gehversuche.py", line 45, in schreibe_woerterbuch
    tag_vokabel = _erstelle_vokabel(schluessel, *wert)
  File "C:\Python26\PyProgramme\xml_gehversuche.py", line 32, in _erstelle_vokabel
    text.data = str(wert)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u0391' in position 0: ordinal not in range(128)
Bei meinen Recherchen zu Codings in xml hab ich nichts hilfreiches gefunden.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Mit `open("...", "w")` schreibst du in eine Textdatei, wobei Strings automatisch in das Encoding deiner Plattform umgewandelt werden. Eine XML-Datei musst du aber als Binärdatei schreiben. Benutze daher `open("...", "wb")`.

Ich würde auch empfehlen, im Programmtext dort, wo du "richtige" Strings hast, auch den String-Datentyp von Python zu benutzen. Bei Python 2.x ist das "unicode", denn "str" ist ein Byte-Array, wo Strings immer in einem bestimmten Encoding drin stehen. Mit der Funktion "str()" willst du unter Python 2.x ein Objekt - möglicherweise einen echten String - in ein Byte-Array konvertieren, was in deinem Fall nicht funktioniert. Unterlasse es. Nimm "unicode" oder lasse diese Zwangskonvertierung einfach weg und übergib `_erstelle_vokabel` Unicode-Strings mit `u"Äther"`.

Stefan
Pascal
User
Beiträge: 271
Registriert: Samstag 4. April 2009, 22:18

Danke. das mit open(..., 'wb') hab ich geändert, sowie das str() durch unicode() ersetzt.
Der UnicodeEncodeError wird jetzt an einer anderen Stelle ausgelöst.

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Python26\PyProgramme\xml_gehversuche.py", line 70, in <module>
    schreibe_woerterbuch(daten, 'griechisch_xml.xml')
  File "C:\Python26\PyProgramme\xml_gehversuche.py", line 54, in schreibe_woerterbuch
    baum.writexml(f, "", "\t", "\n")
 [...]
UnicodeEncodeError: 'ascii' codec can't encode character u'\u0391' in position 3: ordinal not in range(128)
Liegt das jetzt am xml-Modul? Oder muss ich bei mir noch etwas ändern?

Meine Datei sieht jetzt so aus: http://paste.pocoo.org/show/528769/
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dein Quellcode passt nicht zur Fehlermeldung!

Ich würde anstelle des `mindom`-Moduls eher auf die ElementTree-API setzen; ok die Vorteile liegen vor allem beim Parsen und weniger beim Erstellen - aber so lernt man gleich das bessere Werkzeug kennen :-)

Dateien sollte man immer so öffnen:

Code: Alles auswählen

with open(...) as handler:
    # handler hier als File-Object zu verwenden
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Pascal
User
Beiträge: 271
Registriert: Samstag 4. April 2009, 22:18

Hyperion hat geschrieben:Dein Quellcode passt nicht zur Fehlermeldung!
Dafür kann ich ja nichts! Die Ursache für den Fehler scheint jedoch im Modul zu liegen?

ElementTree-API werd ich mir mal anschauen, aber ich fand vom Verständnis her das mindom-Modul ganz nett. Und solange keine Sonderzeichen vorhanden sind funktioniert es sehr gut.

Das open hab ich durch with open as ersetzt.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Pascal hat geschrieben:
Hyperion hat geschrieben:Dein Quellcode passt nicht zur Fehlermeldung!
Dafür kann ich ja nichts! Die Ursache für den Fehler scheint jedoch im Modul zu liegen?
Wer denn sonst? Die Meldung besagt, dass in Zeile 54 etwas schief läuft - der von Dir gezeigte Code zeigt in Zeile 54 allerdings etwas vollkommen anders ;-) Insofern ist es doch legitim nachzufragen!

Schließlich kann es ja sein, dass Du den gezeigten Code gar nicht ausprobiert hast - evtl. funzt der ja schon? Man sollte auf solche "Details" schon achten.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Pascal
User
Beiträge: 271
Registriert: Samstag 4. April 2009, 22:18

Hyperion hat geschrieben:
Pascal hat geschrieben:
Hyperion hat geschrieben:Dein Quellcode passt nicht zur Fehlermeldung!
Dafür kann ich ja nichts! Die Ursache für den Fehler scheint jedoch im Modul zu liegen?
Wer denn sonst? Die Meldung besagt, dass in Zeile 54 etwas schief läuft - der von Dir gezeigte Code zeigt in Zeile 54 allerdings etwas vollkommen anders ;-) Insofern ist es doch legitim nachzufragen!
Ich habe lediglich ein paar Leerzeilen bzw. auskommentierte Zeilen oben rausgenommen. Das ändert nichts daran, dass der Fehler ausgelöst wird. Nur halt in Zeile 49 statt 54, allerdings ist das immer noch die gleiche Stelle.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Wir müssen die Zeile genau wissen, denn die Glaskugel funktioniert im Jahr 2012 nicht so richtig.
the more they change the more they stay the same
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Pascal hat geschrieben: Ich habe lediglich ein paar Leerzeilen bzw. auskommentierte Zeilen oben rausgenommen. Das ändert nichts daran, dass der Fehler ausgelöst wird. Nur halt in Zeile 49 statt 54, allerdings ist das immer noch die gleiche Stelle.
Zeile 49 ist in Deinem Code eine Leerzeile :roll:

Ich habe das ganze mal um ein Encoding und den Shebang ergänzt: Link

Die dazu passende Fehlermeldung lautet:

Code: Alles auswählen

  File "./greek.py", line 64, in <module>
    schreibe_woerterbuch(daten, 'griechisch_xml.xml')
  File "./greek.py", line 48, in schreibe_woerterbuch
    baum.writexml(f, u"", u"\t", u"\n") 
  File "/usr/lib/python2.7/xml/dom/minidom.py", line 1745, in writexml
    node.writexml(writer, indent, addindent, newl)
  File "/usr/lib/python2.7/xml/dom/minidom.py", line 811, in writexml
    node.writexml(writer,indent+addindent,addindent,newl)
  File "/usr/lib/python2.7/xml/dom/minidom.py", line 811, in writexml
    node.writexml(writer,indent+addindent,addindent,newl)
  File "/usr/lib/python2.7/xml/dom/minidom.py", line 811, in writexml
    node.writexml(writer,indent+addindent,addindent,newl)
  File "/usr/lib/python2.7/xml/dom/minidom.py", line 1034, in writexml
    _write_data(writer, "%s%s%s"%(indent, self.data, newl))
  File "/usr/lib/python2.7/xml/dom/minidom.py", line 297, in _write_data
    writer.write(data)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u0391' in position 3: ordinal not in range(128)
Importiere mal das `codecs`-Modul - damit kannst Du ein Encoding bei Dateien angeben und baue Deine `schreibe_woerterbuch` so um:

Code: Alles auswählen

    with codecs.open(dateiname, "w", encoding="utf-8") as handler:
        baum.writexml(handler, u"", u"\t", u"\n") 
Ich würde dennoch zur ElementTree-API raten.

Edit: Ich hab mal ein wenig gespielt und das ganze mit der ElementTree-API umgesetzt: Link.
Leider gibt es kein `prettyprint` im Standard; aber da gibt es ja workarounds. Ansonsten kann man natürlich auch gleich auf lxml setzen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Pascal
User
Beiträge: 271
Registriert: Samstag 4. April 2009, 22:18

wow, vielen Dank!!!

in das ElementTree-Modul werde ich mir nochmals genau anschauen.

und ich werde dann in Zukunft darauf achten, dass auch die Zeilenangabe bei der Fehlermedlung stimmt.


Eine Zeile hab ich jetzt doch noch ergänzt:

Code: Alles auswählen

## Hyperion zu verdanken
Antworten