Lesen und Schreiben von kvtml Vokabeldateien

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.
Ubuntuxer
User
Beiträge: 42
Registriert: Donnerstag 17. April 2008, 15:49

Ich versuche eine kleine Bibliothek zu erstellen, die das Lesen und Schreiben von kvtml Vokabeldateien ermöglicht.
Das kvtml Format ist ein Dateiformat, welches große Ähnlichkeiten mit XML Dateien hat. Das Auslesen und Schreiben sollte deshalb eigentlich problemlos mit der Standartbibliothek xml.etree.ElementTree möglich sein.

Ich bin allerdings auf zwei Probleme gestoßen, die ich trotz durcharbeiten der Referenz zu xml.etree.ElementTree nicht lösen konnte.

1) Strings mit Umlauten werden beim Auslesen automatisch in Unicodes umgewandelt; Wie kann ich die wieder in Utf-8 Strings umwandeln?

2) Wie kann ich die ersten beiden Zeilen der kvtml erstellen, also die Angaben über die xml version und encoding sowie den Doctype.
Ich bin mir mittlerweile ziemlich sicher das es nicht mit der Bibliothek xml.etree.ElementTree geht, aber selbst bei lxml habe ich nichts gefunden.

Mein bisheriger Code sieht so aus:
http://www.python-forum.de/pastebin.php?mode=view&s=4

Eine kvtml Vokabeldatei hat das folgende Format:
http://www.python-forum.de/pastebin.php?mode=view&s=5

PS: Die beiden Dateien die ich hochgeladen habe stehen unter der GPL.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Ubuntuxer hat geschrieben:Wie kann ich die wieder in Utf-8 Strings umwandeln?

Code: Alles auswählen

unicode_string.encode(encoding)
xml version und encoding sowie den Doctype.
Das Erstere sollte eigentlich automatisch dazukommen. Zweiteres musst du wohl von Hand hinzufügen.

Ich empfehle dir auf jeden Fall lxml, weil das Hunderte Male schneller und schlanker ist.
BlackJack

@Ubuntuxer: Warum willst Du die Unicode-Objekte überhaupt als `str` haben? Wenn man mit Zeichen arbeiten will, ist `unicode` doch eigentlich genau das, was man eigentlich möchte!?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ubuntuxer hat geschrieben: Das kvtml Format ist ein Dateiformat, welches große Ähnlichkeiten mit XML Dateien hat.
Das ist xml ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Ubuntuxer
User
Beiträge: 42
Registriert: Donnerstag 17. April 2008, 15:49

Dauerbaustelle hat geschrieben:

Code: Alles auswählen

unicode_string.encode(encoding)
Das Problem ist, dass die HTML Ersatzzeichen für die Umlaute nicht wieder in Umlaute umgewandelt werden.
u'T\xfcr'.encode('utf-8')
'T\xc3\xbcr'
Der KDE Vokabeltrainer Parley kann mit diesen Zeichen nicht umgehen, sodass nur die Sprache ohne Umlaute korrekt aus meiner erzeugten Datei eingelesen wird. :(
@Blackjack: Parley kann auch mit Unicodes nicht umgehen.
xml version und encoding sowie den Doctype.
Das Erstere sollte eigentlich automatisch dazukommen. Zweiteres musst du wohl von Hand hinzufügen.
Und wie füge ich den Doctype per Hand hinzu???
Wenn ich eine Datei einlese und danach neu schreibe sieht sie wie folgt aus:
http://www.python-forum.de/pastebin.php ... &mode=view

Ich empfehle dir auf jeden Fall lxml, weil das Hunderte Male schneller und schlanker ist.[/quote]
Ich meine xml.etree.cElementTree sei noch schneller als lxml, aber das ist mir eigentlich total egal. Hauptsache es funktioniert überhaupt!
BlackJack

@Ubuntuxer: Dann kann Parley nicht mit XML umgehen, was ich jetzt nicht so recht glauben mag, denn dann wäre das Programm für die Tonne.
BlackJack

@Ubuntuxer: Das würde insbesondere auch bedeuten das Parley nicht mit der Datei klarkommt, die Du da gezeigt hast, denn da sehe ich in Zeile 14 bei "Niederländisch" ziemlich deutlich ein 'ä'.

Ich habe da eher den Verdacht das Du etwas falsch machst. XML (das DOM) ist mit Unicode definiert, das kennt kein UTF-8. Die Zeichenketten mit Umlauten, welche Du als `unicode` bekommst, musst Du auch wieder als `unicode` in XML-Element-Objekte reinstecken! *Nicht* als UTF-8 kodierte `str`-Objekte -- da kommt Murks bei raus. Das ist einer der Gründe warum ich gefragt habe, warum Du die überhaupt kodieren willst.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Ubuntuxer, siehe folgende Grafiken (weniger ist schneller ist besser):

Parsing
Bild

Serialization

Bild

(Quelle)
BlackJack

@Dauerbaustelle: Ich zweifle mal die Relevanz an, weil es da ja wohl um potentiell kaputtes HTML geht und mir zumindest jetzt nicht klar ist, wieviel zusätzlichen Aufwand das jeweils macht das vorzuverarbeiten oder irgendwie anders beim Parsen zu berücksichtigen.

Kann ja zum Beispiel sein dass die beim reinen Parsen vom XML alle gleich schnell sind, sich aber in der "Korrekturphase" vorher unterschiedlich lange aufhalten.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

lxml hat ein eingebautes html-Modul, das das Parsen von kaputten HTML ziemlich effizient managed.
BlackJack

@Dauerbaustelle: Das untermauert, dass die Zahlen hier wertlos sind, weil der Unterschied zu einem relevanten Anteil im HTML-Teil liegen könnte. Um HTML geht es hier aber gar nicht.
Ubuntuxer
User
Beiträge: 42
Registriert: Donnerstag 17. April 2008, 15:49

BlackJack hat geschrieben:@Ubuntuxer: Das würde insbesondere auch bedeuten das Parley nicht mit der Datei klarkommt, die Du da gezeigt hast, denn da sehe ich in Zeile 14 bei "Niederländisch" ziemlich deutlich ein 'ä'.

Ich habe da eher den Verdacht das Du etwas falsch machst. XML (das DOM) ist mit Unicode definiert, das kennt kein UTF-8. Die Zeichenketten mit Umlauten, welche Du als `unicode` bekommst, musst Du auch wieder als `unicode` in XML-Element-Objekte reinstecken! *Nicht* als UTF-8 kodierte `str`-Objekte -- da kommt Murks bei raus. Das ist einer der Gründe warum ich gefragt habe, warum Du die überhaupt kodieren willst.
Ich versuche einfach mal noch genauer zu beschreiben wo mein Problem liegt:

Ich parse zunächst die Orginal kvtml Datei von Parley und dabei z.B. das Wort "Niederländisch"
Die Bibliothek xml.etree.cElementTree wandelt seltsamerweise Wörter mit Umlauten in einen Unicode um, während Wörter ohne Umlaut normale Strings bleiben.
Dies bedeutet das Wort Niederländisch wird zu u'Niederl\xe4ndisch'.
Ich könnte das eingelesene Wort direkt in einen String umwandeln.
u'Niederl\xe4ndisch'.encode('utf-8') => 'Niederl\xc3\xa4ndisch'
Nun möchte ich die eingelesen Informationen wieder in eine neue kvtml Datei schreiben.

Code: Alles auswählen

text = ETree.SubElement(entry, "text")
text.text = u'Niederl\xe4ndisch'
Dann steht in meiner neuen kvtml Datei <text>u'Niederl\xe4ndisch'</text>.

Code: Alles auswählen

text = ETree.SubElement(entry, "text")
text.text = u'Niederl\xe4ndisch'.encode('utf-8')
Dann steht in meiner neuen kvtml Datei <text>'Niederl\xc3\xa4ndisch'</text>
Beides kann Parley nicht lesen.

Ich vermute einfach mal, dass xml.etree.cElementTree die Information braucht, wie es die zugewiesen Unicodes encoden soll. In der Orginaldatei steht ja auch: <?xml version="1.0" encoding="UTF-8"?>

Ohne die beiden Zeilen ersten Zeilen der Orginaldatei kann Parley meine erstellte Datei überhaupt nicht lesen.

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE kvtml PUBLIC "kvtml2.dtd" "http://edu.kde.org/kvtml/kvtml2.dtd">
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

BlackJack hat geschrieben:Um HTML geht es hier aber gar nicht.
Du hast aber was davon erzählt o_O
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Also mit lxml kein Problem:

Code: Alles auswählen

In [17]: from lxml import etree

In [18]: with open("kvtml.xml", "r") as infile:
    tree = etree.parse(infile)
   ....:     
   ....:     

In [20]: tree
Out[20]: <lxml.etree._ElementTree object at 0xb75adecc>

In [21]: tree.docinfo
Out[21]: <lxml.etree.DocInfo object at 0xb72eea34>

In [22]: tree.docinfo.doctype
Out[22]: u'<!DOCTYPE kvtml PUBLIC "kvtml2.dtd" "http://edu.kde.org/kvtml/kvtml2.dtd">'

In [23]: tree.docinfo
tree.docinfo

In [23]: tree.docinfo.
tree.docinfo.URL               tree.docinfo.__reduce__        tree.docinfo.externalDTD
tree.docinfo.__class__         tree.docinfo.__reduce_ex__     tree.docinfo.internalDTD
tree.docinfo.__delattr__       tree.docinfo.__repr__          tree.docinfo.public_id
tree.docinfo.__doc__           tree.docinfo.__setattr__       tree.docinfo.root_name
tree.docinfo.__format__        tree.docinfo.__sizeof__        tree.docinfo.standalone
tree.docinfo.__getattribute__  tree.docinfo.__str__           tree.docinfo.system_url
tree.docinfo.__hash__          tree.docinfo.__subclasshook__  tree.docinfo.xml_version
tree.docinfo.__init__          tree.docinfo.doctype           
tree.docinfo.__new__           tree.docinfo.encoding          

In [23]: text = etree.Element("text")

In [24]: text.text = u"Niederländisch"

In [25]: text.text
Out[25]: u'Niederl\xc3\xa4ndisch'

In [26]: print text.text.encode("latin-1")
-------> print(text.text.encode("latin-1"))
Niederländisch
(Bei mir ist in der Shell wohl latin-1 als default encoding eingestellt. Muss ich mal nachprüfen... ist ja vollkommen ätzend!)

Aber wie Du siehst kümmert sich lxml auch um Header-Infos. Kann mir aber nicht vorstellen, dass die ElementTree-API aus der Standard-Lib das nicht tut.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Ubuntuxer: Das Zeichenketten mit Umlauten als `unicode`-Objekte von den diversen XML-Bibliotheken geliefert werden ist nicht seltsam sondern das normale Verhalten. Seltsam könnte man eher das Verhalten bei Zeichenketten nennen, die nur ASCII-Zeichen enthalten. Das wird so gemacht um Speicher zu sparen und weil man ASCII-Zeichenketten problemlos mit Unicode-Objekten "mischen" kann. Denn die können im Bedarfsfall ohne Fehler automatisch in Unicode-Objekte umegewandelt werden.

Du machst irgendwo etwas fundamental falsch, denn die gezeigten Quelltextschnippsel können nicht zu dem behaupteten Ergebnis führen.

Code: Alles auswählen

In [571]: import xml.etree.cElementTree as ETree

In [572]: text = ETree.Element('text')

In [573]: text.text = u'Niederl\xe4ndisch'

In [574]: ETree.tostring(text)
Out[574]: '<text>Niederländisch</text>'
Zeig mal bitte ein minimales Beispiel wie Du da ``<text>u'Niederl\xe4ndisch'</text>`` herausbekommen haben willst ohne irgendwann mal `repr()` die Daten anzuwenden.

@Dauerbaustelle: Ich habe nichts von HTML erzählt. Schon gar nichts was HTML-Parser hier irgendwie ins Spiel gebracht hätte.
Ubuntuxer
User
Beiträge: 42
Registriert: Donnerstag 17. April 2008, 15:49

Uups, entschuldige ich hab da irgendetwas durcheinandergebracht.
Also es sieht bei mir so aus:
Orginal:
<name>Niederländisch</name>
Bei u'Niederl\xe4ndisch':
<name>Niederl&#228;ndisch</name>
Bei 'Niederl\xc3\xa4ndisch': (u'Niederl\xe4ndisch'.encode('utf-8'))
<name>Niederl&#195;&#164;ndisch</name>
BlackJack

@Ubuntuxer: Und wo liegt jetzt das Problem? ``<name>Niederl&#228;ndisch</name>`` ist gültiges XML das genau das gewünschte darstellt.
Ubuntuxer
User
Beiträge: 42
Registriert: Donnerstag 17. April 2008, 15:49

BlackJack hat geschrieben:@Ubuntuxer: Und wo liegt jetzt das Problem? ``<name>Niederl&#228;ndisch</name>`` ist gültiges XML das genau das gewünschte darstellt.
Ich habe gerade gemerkt, dass das tatsächlich ohne Probleme funktioniert.
Parley konnte meine erstellte Datei aus einem anderen Grund nicht lesen. Anscheinend werden die Dateien von Parley irgendwie gecacht, sodass wenn ich die erstellte Datei ohne die beiden ersten Zeilen öffne und danach die Zeilen einfüge und noch einmal die Datei mit Parley öffne nur eine Sprache angezeigt wird. Ich dachte die ganze Zeit dies läge an den Unicodes, da die deutschen Wörter in Parley nicht angezeigt wurden. :?

Damit hat sich das Unicode Problem erledigt. Danke für deine Geduld Blackjack.

Hat einer noch eine Idee wie ich das Problem mit den ersten beiden Zeilen lösen kann?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ubuntuxer hat geschrieben: Hat einer noch eine Idee wie ich das Problem mit den ersten beiden Zeilen lösen kann?
Hast Du meinen Post überlesen?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Ubuntuxer
User
Beiträge: 42
Registriert: Donnerstag 17. April 2008, 15:49

Hyperion hat geschrieben:
Ubuntuxer hat geschrieben: Hat einer noch eine Idee wie ich das Problem mit den ersten beiden Zeilen lösen kann?
Hast Du meinen Post überlesen?
Ich möchte den Doctype nicht auslesen, sondern in eine neue kvtml Datei reinschreiben. Mit lxml kann man zumindestens den Doctype auslesen, aber ich habe mir auch die lxml Referenz angeschaut und keine Erklärung gefunden wie man den Doctype in eine neue xml schreiben kann.

Code: Alles auswählen

from lxml import etree
root = etree.Element("kvtml")
root.set("version", "2.0")
tree = etree.ElementTree(root)
tree.docinfo.doctype = u'<!DOCTYPE kvtml PUBLIC "kvtml2.dtd" "http://edu.kde.org/kvtml/kvtml2.dtd">'
AttributeError: attribute 'doctype' of 'lxml.etree.DocInfo' objects is not writable
Antworten