Encoding Probleme mit minidom

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.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hallo zusammen,

ich weiß, dass der Titel schlimmes ahnen lässt und das dieses Thema in unzähligen Threads behandelt wurde und alle Regulars hier sicherlich nerven wird, aber leider habe ich auch nach dem Studium von zig Tutorials und Dokus wohl einiges noch nicht abschließend kapiert.

Kurz etwas zum Problem: Ich bin dabei ein kleines CGI-Script zu schreiben, das auf meinem Uni-Webspace laufen soll. Auf der Maschine verrichtet ein Apache seinen Dienst und das ganze läuft auf einem RedHat System. Python liegt dort (nur) in Version 2.3 vor.

Nun habe ich dort leider massive Codierungsprobleme beim Auswerten und Speichern der Userdaten. Am Schluss soll es eine Ausgabe auf einer HTMl-Seite geben und die Daten sollen per minidom in ein XML-File geschrieben werden. So viel zum "eigentlichen" Problem.

So, ich habe nun mal versucht mich der Sache einfach per Shell zu nähern und einfach mal verschiedene Sachen auszuprobieren, vor allem Dinge aus dem umfangreichen Tutorial http://www.python-forum.de/viewtopic.php?p=30572#30572, welches hier gerne als Hilfe angegeben wird.

Im folgenden mal die Auszüge aus der Python-Shell auf dem Uni-Rechner.

Code: Alles auswählen

>>> s = "Österreich"
>>> print s
Österreich
Na das klappt ja :)

Code: Alles auswählen

>>> s.decode("ascii")
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)
Also bedeutet das, dass der Ursprungszeichensatz gar kein "ascii" war? Dies wäre eigentlich kein Problem, wenn ich nicht folgendes rausgefunden hätte:

Code: Alles auswählen

>>> sys.getdefaultencoding()
'ascii'
Beduetet das nicht, dass diese Python-Instanz ascii-Kodierung verwendet?
Aber wie kann ich dann überhaupt ein "Österreich" eintippen?

Ok, dann habe ich mal folgendes gemacht:

Code: Alles auswählen

>>> s.decode("utf-8")
u'\xd6sterreich'
Sieht nach Unicode aus, oder?

Code: Alles auswählen

>>> s.decode("utf-8").encode("utf-8")
'\xc3\x96sterreich'
Wieso wandelt er nun das "\xd6" also das "Ö" nicht wieder in ein "Ö"?
Und das geht auch nicht!

Code: Alles auswählen

>>> s.decode("utf-8").encode("ascii")
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
UnicodeEncodeError: 'ascii' codec can't encode character u'\xd6' in position 0: ordinal not in range(128)
Aber ascii soll doch angeblich der defaultencoding sein ... ich blicks einfach nicht (und ja, in ascii gibt es keine "ä","ö" usw ... daher bin ich noch verwirrter wie das alles zusammenhängt!)

Code: Alles auswählen

>>> sys.getfilesystemencoding()
'UTF-8'
Das besagt doch, dass die Parameter des OS in utf-8 übergeben werden, oder? Also sollten doch meine Texte aus dem CGI-Script und dem zugehörigen textarea als utf-8 übergeben werden, oder?

Ich habe also mal folgendes kleine Script geschrieben, um ein paar Sachen zu testen:

Code: Alles auswählen

import sys

print "default:",sys.getdefaultencoding()
print "filesystem:",sys.getfilesystemencoding()
print
print "Parameter:",sys.argv[1]

try:
	print "von utf-8",sys.argv[1].decode("utf-8")
except Exception, e:
	print e

try:
	print  "von ascii",sys.argv[1].decode("ascii")
except Exception, e:
	print e

try:
	print  "von iso-8859-15",sys.argv[1].decode("iso-8859-15")
except Exception, e:
	print e
Hier die Ausgabe: (lokal erhalte ich sogar dieselbe)

Code: Alles auswählen

as:> python parameter.py ä
default: ascii
filesystem: UTF-8

Parameter: ä
von utf-8 ä
von ascii 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)
von iso-8859-15 À
Wenn ich das richtig begreife bekommt also Python den Übergabeparameter tatsächlich als utf-8. Wieso aber meckert dann mein Script, wenn es "ä", "ö" usw. erhält?

Hier mal der Auszug von der Stelle, an der der fehler auftritt:
(doc ist das xml-Dokument per minidom erstellt)

Code: Alles auswählen

	try:
		tmp = doc.toxml()
		print tmp
		#f = codecs.open(file,"w","utf-8")
		#f.write(tmp)
		#f.close()
	except Exception, e:
		drawError("Fehler beim Speichern der aktuellen Sprüche",e)
		sys.exit(1)
(Die Kommentare sind nur "Bastel"-Sachen, die ich mal auskommentiert habe. Der Fehler liegt also def. am toxml() von minidom!

Code: Alles auswählen

'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
Bekomme ich als Fehler, Eingabe war "ääööö"

Ich bin irgend wie mit meinem Latein am Ende!
Thomas W.
User
Beiträge: 17
Registriert: Sonntag 9. April 2006, 08:36
Wohnort: Halle (Saale)

Ich will mich erst noch mit Unicode und UTF beschäftigen und kann deshalb Deine Fragen nicht beantworten. Eine gute Übersicht habe ich aber schon mal unter [wiki]Von Umlauten, Unicode und Encodings[/wiki] gefunden. Vielleicht hilft sie Dir weiter, Unicode, UTF und Encodings zu verstehen. Ansonsten sie mal unter http://www.python-forum.de/topic-6539.html nach.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Danke, der erste Link ist mir bekannt, ist quasi derselbe Beitrag aus dem Board hier ;)

Werd das zweite mal antesten ... wobei es bei mir ja keine Darstellungsprobleme gibt, sondern interne Fehler ...
BlackJack

Ich weiss nicht ob man da überhaupt noch was sagen kann/soll. Du hast Unicode nicht verstanden und es gibt in der Tat genug Beiträge dazu.

Nur mal so um das erste Missverständnis auszuräumen:

Code: Alles auswählen

s = 'Österreich'
`s` enthält hier keine Buchstaben sondern irgendwelche Bytes die als Buchstaben interpretiert werden können. Einfach zu sagen `s` fängt mit einem 'Ö' an ist falsch, weil man das nicht weiss! Es kommt immer darauf an wie die Zeichenkette kodiert ist und ob das die Kodierung ist, von der man ausgeht.

Bei Dir sind das zum Beispiel zwei Bytes die das 'Ö' ausmachen, weil Deine Zeichenkette offenbar UTF-8 kodiert ist.

XML ist Unicode, also verwende dort am besten auch Unicode.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:Ich weiss nicht ob man da überhaupt noch was sagen kann/soll. Du hast Unicode nicht verstanden und es gibt in der Tat genug Beiträge dazu.
Ja, aber die haben mir nicht geholfen ;)
Nur mal so um das erste Missverständnis auszuräumen:

Code: Alles auswählen

s = 'Österreich'
`s` enthält hier keine Buchstaben sondern irgendwelche Bytes die als Buchstaben interpretiert werden können. Einfach zu sagen `s` fängt mit einem 'Ö' an ist falsch, weil man das nicht weiss! Es kommt immer darauf an wie die Zeichenkette kodiert ist und ob das die Kodierung ist, von der man ausgeht.
Wo habe ich geschrieben, dass s mit "Ö" anfängt? Mir geht es ja grad darum, wie s codiert wird!
Bei Dir sind das zum Beispiel zwei Bytes die das 'Ö' ausmachen, weil Deine Zeichenkette offenbar UTF-8 kodiert ist.
Wie ich ja auch vermutet hatte ...
XML ist Unicode, also verwende dort am besten auch Unicode.
Ja genau das klappt ja leider nicht! Die Methode .toxml() wirft ja grad die Exception mit dem Codierungsfehler! Ist mir leider ein Rätsel, weil die Zeichenkette ja über das Webinterface in das Script gelangt und somit ja utf-8 sein sollte!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

So, ich habe mal folgendes minimal Test-Script geschrieben, um zu sehen, ob ich den Fehler reproduzieren kann:

Code: Alles auswählen

#! /usr/bin/python
# -*- coding: utf-8 -*-
from xml.dom.minidom import getDOMImplementation
import sys

def main(args):
	impl = getDOMImplementation()
	doc = impl.createDocument(None, "test", impl.createDocumentType("noNamespaceSchemaLocation",None,None))
	root = doc.documentElement
	text = doc.createTextNode(args[1])
	root.appendChild(text)
	print doc.toprettyxml()

if __name__ == "__main__":
	main(sys.argv)
Wenn ich das nun auf dem Server aufrufe, bekomme ich folgendes korrektes Ergebnis:

Code: Alles auswählen

as:> ./mindom-example.py halloäöüö
<?xml version="1.0" ?>
<!DOCTYPE noNamespaceSchemaLocation>
<test>
        halloäöüö
</test>
Jetzt bin ich entgültig verwirrt! Wieso klappt das damit? Die einzige Schlussfolgerung, die ich im Moment ziehen kann ist folgende: Der Apache übergibt dem Script die Parameter eben nicht im "utf-8" ... aber wie kann das sein? Und vor allem, wie denn sonst?
BlackJack

Hyperion hat geschrieben:
XML ist Unicode, also verwende dort am besten auch Unicode.
Ja genau das klappt ja leider nicht! Die Methode .toxml() wirft ja grad die Exception mit dem Codierungsfehler! Ist mir leider ein Rätsel, weil die Zeichenkette ja über das Webinterface in das Script gelangt und somit ja utf-8 sein sollte!
Ich sagte verwende Unicode, nicht UTF-8. Das ist nicht das gleiche!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

So, ich habe das Test-Script mal so umgestellt, dass es einen Parameter per CGI bekommt und diesen als TextNode einträgt.

Rufe ich das Script dann per Browser mit Parameter auf, so kann er auch Umlaute korrekt darstellen! (genauer .toxml() meckert nicht rum!)

Genau diesen Codeschnipsel habe ich nun mal in mein eigentlichs Script an die Stelle des Speicherns gesetzt und den alten Code auskommentiert. Ergebnis: Es kommt wieder er coding Fehler!

Wie kann das nun sein? Fakt ist doch, dass beide Scripte an den imho entscheidenden Stellen exakt identischen Code haben und der Aufruf ebenfalls identisch ist. Wieso geht es bei dem einen, beim anderen kommt dieser scheiß codierungs Fehler?

Auf Wunsch kann ich den kompletten Code gerne posten, aber da der doch relativ lang ist, hoffe ich erst einmal auf einen dummen Bock meinerseits, der einfach zu erkennen ist.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben: Ich sagte verwende Unicode, nicht UTF-8. Das ist nicht das gleiche!
Ok, also muss ich an einer Stelle wohl einen .decode("utf-8") einfügen, um die vermeindliche utf-8 zeichenkette explizit in unicode zu wandeln, korrekt?
BlackJack

Falls es UTF-8 ist, sollte das gehen. Ich bin jetzt zu faul in den RFCs zu wühlen, aber soweit ich das in Erinnerung habe antworten Browser mit der Kodierung in der die Seite bei ihnen ankam.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:Falls es UTF-8 ist, sollte das gehen. Ich bin jetzt zu faul in den RFCs zu wühlen, aber soweit ich das in Erinnerung habe antworten Browser mit der Kodierung in der die Seite bei ihnen ankam.
Leider geht das ja genau nicht! Daher habe ich die Test-Scripte geschrieben, um das auszutesten.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Und warum kein Ö angezeigt wird, wenn dein "Österreich" in in Unicode enkodiert und dann in UTF-8 kodiert wird ist auch einfach zu erklären: ``repr()`` gibt bei Unicode-Strings immer "Sonderzeichen" als ``\x??``-Sequenzen aus, Wenn du einen Unicode-String direkt im Interpreter angibst, bekommst du die Ausgabe von ``repr()``. Du musst schon ``print`` verwenden.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Danke, das ist mal eine gute Erklärung :)

Und das andere Problem? Irgend eine Ahnung, wie ich den form-Parameter nun in unicode wandeln kann?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

*push*
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Oh, sorry, habe deinen Post ganz übersehen,
Hyperion hat geschrieben:Und das andere Problem? Irgend eine Ahnung, wie ich den form-Parameter nun in unicode wandeln kann?
Form-Parameter?

Du kannst Bytestrings alle mit ``bytestringobjekt.encode('encoding_des_bytestrings')`` zu machen (wobei ``encode()`` natürlich einen Unicode-String zurückgibt und nicht den Bytestring ändert, versteht sich).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Danke Dir :) Werd ich morgen mal probieren!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Leonidas hat geschrieben:Oh, sorry, habe deinen Post ganz übersehen,
Hyperion hat geschrieben:Und das andere Problem? Irgend eine Ahnung, wie ich den form-Parameter nun in unicode wandeln kann?
Form-Parameter?

Du kannst Bytestrings alle mit ``bytestringobjekt.encode('encoding_des_bytestrings')`` zu machen (wobei ``encode()`` natürlich einen Unicode-String zurückgibt und nicht den Bytestring ändert, versteht sich).
Naja, man muss die Parameter des form per .decode("kodierung") in unicode wandeln ;) Egal, jetzt hab ichs kapiert.

Dennoch stellt sich mir nun eine Frage: Wie kann ich denn bitte bei einem CGI-Script sicherstellen, dass das richtige "Coding" ankommt bei meinem Script? Ok, beim form-Tag in HTML kann man die Kodierung zwar einstellen, aber erstens wird das sicher nicht durch jeden Browser unterstützt und zweitens kann man ein Script ja auch umgehen! Gibt es für dieses Problem eine Lösung?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Gegen mutwillig falsche Encodings kannst du nichts machen. Du kannst versuchen das Encoding zu raten, aber sauber wäre es einfach dem Formular zu sagen, dass du ein bestimmtes Encoding erwartest und fertig. Man kann nicht auf jede erdenkliche Blödheit von Browsern reagieren (wobei ich jetzt nicht wüsste welcher halbwegs aktuelle Browser kein UTF-8 schicken könnte). Letztendlich ist ein falsches Encoding auch kein Beinbruch, die Umlaute sehen dann eben zerhackt aus, aber es ist kein Sicherheitsproblem und man könnte es auch in Einzelfällen manuell wieder richten indem man es umkodiert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ok, danke! Prinzipiell hast Du damit wohl Recht. Kann man denn ein Script zum Absturz bringen, wenn man bewusst in ner falschen Zeichencodierung sendet? Also wenn ich UTF-8 erwarte und man mir sonst was schickt ... oder ist es prinzipiell sinnvoll utf-8 zu erwarten?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Zum Absturz bringen: ja, du kannst einen UnicodeDecodeError bekommen, wenn du versuchst einen Unicode-String in in ein Encoding zu kodieren welches nicht alle Zeichen des Unicode-Strings darstellen kann. Häufigstes Beispiel: Unicode-Strings mit Umlauten werden versucht in ASCII zu kodieren - kann nicht funktionieren.

Aber da musst du eben aufpassen, dass du intern Unicode verwendest und extern UTF-8 erwartest und das auch rausgibst. UTF-8 ist definitiv eine gute Wahl, für ein Datenaustausch-Encoding.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten