Unicode-Fehler bei Umleitung in Datei (Linux) [solved]

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
felicehome
User
Beiträge: 3
Registriert: Donnerstag 25. Mai 2006, 14:10

Hallo allerseits,

eigentlich kenne ich mich mit der Thematik Unicode in Python, auch dank gerold's super Tutorial http://www.python-forum.de/viewtopic.php?p=30740& ganz gut aus.
Allerdings habe ich ein sehr spezielles Problem bei der Umleitung einer Ausgabe in eine Datei.
Zur Demonstration des Problems gebe ich hier einfach mal ein Code-Beispiel. (Wie heißt es so schön: ein code sagt mehr als 1000 Worte :D)

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys

#print u'\xb7' #hier weggelassen, da sonst Fehler bei Umleitung in Datei
print "stdout.encoding: ", sys.stdout.encoding
print "filesystemencoding: ", sys.getfilesystemencoding()
wenn ich das Skript normal ausführe also:

Code: Alles auswählen

./testenc.py
bekomme ich folgende Ausgabe:

Code: Alles auswählen

stdout.encoding:  UTF-8
filesystemencoding:  UTF-8
wenn ich also in Zeile 5 den Kommentar weglasse wird dieses Zeichen (midpoint) auch korrekt ausgegeben und dargestellt.

Leite ich nun die stdout in eine Datei um also:

Code: Alles auswählen

./testenc.py > test.txt
sieht die Datei dannach folgendermaßen aus:

Code: Alles auswählen

stdout.encoding:  None
filesystemencoding:  UTF-8
und wenn ich nun Zeile 5 den Kommentar wegnehme bekomme ich natürlich folgenden Fehler:

Code: Alles auswählen

UnicodeEncodeError: 'ascii' codec can't encode character u'\xb7' in position 0: ordinal not in range(128)
Ich bin fast am Verzweifeln, da ich es seit Tagen nicht schaffe dem Fehler auf die Spur zu kommen. Jede Hilfe ist herzlichst willkommen.

Danke
und an alle Väter hier einen Glückwunsch zum Vatertag

Gruß Felice
Zuletzt geändert von felicehome am Donnerstag 25. Mai 2006, 17:49, insgesamt 1-mal geändert.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

felicehome hat geschrieben:Allerdings habe ich ein sehr spezielles Problem bei der Umleitung einer Ausgabe in eine Datei.
Hi Felice!

Vielleicht kannst du daraus etwas ablesen.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys

out_encoding = sys.stdout.encoding or sys.getfilesystemencoding()
fs_encoding = sys.getfilesystemencoding()

print "stdout.encoding: ", out_encoding
print "filesystemencoding: ", fs_encoding

print u'\xb7'.encode(out_encoding)
print "Österreichische Spaßmacher".decode("utf-8").encode(out_encoding)
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
felicehome
User
Beiträge: 3
Registriert: Donnerstag 25. Mai 2006, 14:10

Hallo Gerold,

erstmal vielen Dank für deine prompte Hilfe.
Leider konnte ich aus deinem Skript keine weitere Informationen für die Lösung des Problems finden.
Nach stundenlangem Feiertags-googeln auf englisch habe ich schließlich die Lösung für dieses Problem gefunden:

http://wiki.python.org/moin/ShellRedirectionFails

Es hat tatsächlich damit zu tun, dass Python die Kodierung des Zeichensatzes unterschiedlich behandelt, je nachdem ob stdout in eine Datei umgeleitet wird oder nicht.
Bei Zeiten könntest du das ja in deinem ohnehin schon besten deutschsprachigem Tutorial über "Python und Unicode" ergänzen.
Tatsächlich habe ich dieses Forum erst über google gefunden, nachdem ich verzweifelt versucht habe dem Problem auf die Spur zu kommen.
Dieses Forum ist einfach Klasse!
Weiter so!

Danke Felice
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

felicehome hat geschrieben:Leider konnte ich aus deinem Skript keine weitere Informationen für die Lösung des Problems finden.
Nach stundenlangem Feiertags-googeln auf englisch habe ich schließlich die Lösung für dieses Problem gefunden:
http://wiki.python.org/moin/ShellRedirectionFails
[...]
Bei Zeiten könntest du das ja in deinem ohnehin schon besten deutschsprachigem Tutorial über "Python und Unicode" ergänzen.
Hi Felice!

Schade, ich dachte das Beispiel erklärt es recht gut. Zwei wichtige Aussagen wollte ich mit dem Beispiel erklären. Ich hätte vielleicht doch darauf hinweisen sollen. ;-) Es fällt einem beim Schreiben nicht auf.

1.) Die Encodings können sich unterscheiden, es kann sogar sein, dass sich das Encoding nicht über **sys.stdout.encoding** herausfinden lässt. Meistens dann, wenn man das Skript innerhalb einer IDE ausführt. Für diesen Fall gibt es einen Rückfallwert --> **sys.getfilesystemencoding()**

2.) Man muss die Ausgabe in das Encoding von **sys.stdout** umwandeln. Dafür muss zuerst der auszugebende Text nach Unicode und dann in das Ziel-Encoding umgewandelt werden. Verwendet man das kleine "u" um einen String schon bei der Erstellung nach Unicode zu wandeln, dann entfällt die umständliche Decodierung.

Aber es hatte doch ein Gutes. Du hast gegoogelt und eine gute Idee zu tage gebracht. Statt dem umständlichen Encoden bei jedem **print**, einfach **sys.stdout** über einen **StreamWriter** umzuleiten, das finde ich so gut, dass ich das demnächst ins Tutorial mit rein schreiben werde. Vielen Dank für die Idee und danke für das Lob.

Hier das Beispiel, das ich schon oben geschrieben habe, diesmal aber mit dem Unterschied, dass vorher schon **sys.stdout** über einen StreamReader umgeleitet wird. Wie man sieht, entfällt jedes **.encode(out_encoding)**.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import codecs

# Encodings raus finden
out_encoding = sys.stdout.encoding or sys.getfilesystemencoding()
fs_encoding = sys.getfilesystemencoding()

# StdOut -> Streamwriter mit korrekten Encoding
sys.stdout = codecs.getwriter(out_encoding)(sys.__stdout__)

print "stdout.encoding: ", out_encoding
print "filesystemencoding: ", fs_encoding

print u'\xb7'
print "Österreichische Spaßmacher".decode("utf-8")
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
felicehome
User
Beiträge: 3
Registriert: Donnerstag 25. Mai 2006, 14:10

Hi Gerold,

jetzt muss ich dich schon wieder nerven.
Schade, ich dachte das Beispiel erklärt es recht gut. Zwei wichtige Aussagen wollte ich mit dem Beispiel erklären. Ich hätte vielleicht doch darauf hinweisen sollen. Wink Es fällt einem beim Schreiben nicht auf.
Du hast natürlich recht! Deine erste Antwort hätte mein Problem bereits gelöst. In meiner unendlichen Ignoranz hab ich mich darauf verlassen, dass meine Python Kenntnisse ausreichen um dein Sourcecode ohne ausprobieren zu verstehe und es wie du es so schön sagtest "rauslesen kann". Ich dachte das dein Beispiel den selben Fehler liefern würde, wenn ich es in eine Datei umleite.

Was mich wohl an der ganzen Sache so verwirrte, dass meine shell das ganze ohne den Umweg nach utf-8 korrekt ausgibt. Warum eigentlich? Versteht Bash von Haus aus Unicode? Oder wie macht es dann Python, dass ein Unicode String, der noch nicht nach utf-8 umgewandelt wurde korrekt auf der bash ausgibt? Deshalb bin ich davon ausgegangen, dass mein Codebeispiel bereits utf-8 explizit ausgibt und sich nichts an meinem Problem ändert durch deine Erklärung. Sorry nochmal für diese unglaublich dumme Behauptung.
Aber es hatte doch ein Gutes. Du hast gegoogelt und eine gute Idee zu tage gebracht.
Es hatte noch was gutes. Ich glaube ich habe es endlich halbwegs begriffen!!! :D

Vielleicht habe ich noch eine Ergänzung die du gebrauchen kannst.
Diese ganze Reise durch den Unicode Dschungel mache ich eigentlich, weil ich gerade mit pygoogle, einem wrapper für die google api experimentiere. pygoogle importiert unter anderem soapbuilder.py, welches das encoding mittels dem veraltetem sys.getdefaultencoding() ermittelt. (Wie ich gelesen habe soll diese Funktion in der nächsten Python Version rausfallen). Komischerweise ?? liefert diese funktion als Rückgabewert ("ASCII"), was dann natürlich zu Problemen führt.
Und so ändert man den Rückgabewert von sys.getdefaultencoding():
(was du wahrscheinlich als unicode-guru längst weißt)

Code: Alles auswählen

# sitecustomize.py                   1
# this file can be anywhere in your Python path,
# but it usually goes in ${pythondir}/lib/site-packages/
import sys
sys.setdefaultencoding('utf-8')
dabei ist sitecustomize.py ein spezielles skript, dass beim starten von python importiert wird.

Dieses sys.setdefaultencoding(enc_string) funktioniert auch nur in diesem speziellen sitecustomize.py.
Wenn man sys mittels "import sys" in einem eigenen skript importiert ist setdefaultencoding(enc_string) nicht verfügbar.

Ist ganz nützlich, wenn man alte Bibliotheken hat, die sich bei Ermittlung der Kodierung auf sys.getdefaultencoding() verlassen und man dieselbige dann "zwingen" muss ein anderes defaultencoding zu verwenden.

Vielen Dank nochmal für deine Mühe und ausführliche Erklärung, die du dir eigentlich hättest sparen können, wenn ich mir deine erste Antwort nur genauer angeschaut hätte. :oops:

Gruß Felice
BlackJack

felicehome hat geschrieben:Was mich wohl an der ganzen Sache so verwirrte, dass meine shell das ganze ohne den Umweg nach utf-8 korrekt ausgibt. Warum eigentlich? Versteht Bash von Haus aus Unicode? Oder wie macht es dann Python, dass ein Unicode String, der noch nicht nach utf-8 umgewandelt wurde korrekt auf der bash ausgibt?
Python schaut sich beim Ausgeben das encoding-Attribut des Dateiobjektes an. Wenn das Dateiobjekt an das "echte" stdout gebunden ist, dann sollte diese Kodierung der Deiner Umgebung entsprechen, unter Linux also das was in LANG oder LC_LANG & Co steht. Eine Unicode-Zeichenkette wird dann vor der Ausgabe einfach in diese Kodierung umgewandelt. Und Deine Bash kann UTF-8.

Wenn Du die Ausgabe in eine Datei umleitest, dann hat `sys.stdout` keine bevorzugte Kodierung mehr, weil es keine Standardkodierung für Dateiinhalte gibt. Das muss der Anwender oder die Anwendung entscheiden können.
Antworten