unicode und encodings, wie so oft..

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
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Hallo, ich mal wieder. :)

Diesmal zwei kleine Fragen zu Unicode und utf-8.

Ich habe folgendes:
- einen Unicode-String (ja, wirklich)
- und ein externes Programm das diesen string als argument erhält

Mein Problem:
Im String ist ein ä, ich encodiere ihn mit utf-8, verbinde ihn mit weiteren utf-8 strings und starte das programm.
Doch das Programm verweigert den Start, weil das ä kein ä ist, sondern \xc3\xa4.

Ist wohl nurn Verständnisproblem, blicke zwar inzwischen "ganz grob" durch bei diesem Thema, aber scheinbar halt doch noch nicht so perfekt.

Deshalb nun die Fragen:
- Das "ä" ist der Char(akter) bzw. Glyph zur grafischen Darstellung, also dem was "ich" sehe. Das utf-8 Code-Point Äquivalent \xc3\xa4 ist das was python "sieht" und auch weitergibt wenn ich mit subprocess das Programm starte, richtig?
- Angenommen es ist richtig, wie sag ich dann python es soll nicht den Code-Point sondern den Charakter mit subprocess verwenden?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Zunächst einmal viel Lektüre, wie immer bei dem Thema:
http://wiki.python-forum.de/Von%20Umlau ... 0Encodings
http://wiki.python-forum.de/User%20Grou ... folien.pdf

Weißt Du denn, welches Encoding das externe Programm erwartet? Vermutlich kann es eben nicht mit uft-8 umgehen...

Generell solltest Du intern solange wie möglich mit unicode arbeiten und nicht mit Byte-Strings, wie Du es beschrieben hast. Konkateniere alles in Unicode und wandle erst ganz am Schluss in ein passendes Encoding.

Zu den Fragen am Schluss: Python gibt eben einen Bytestring in utf-8 codierter Form an das andere Programm weiter. Das "ä" ist nur ein Zeichen, welches eine Byte-Sequenz im Bytestring als durckbares Zeichen darstellt. Im Rechner werden aber eben Bytes weitergegeben, keine druckbaren Zeichen. Insofern kannst Du Python da nichts anderes beibringen. Aber Du kannst eben prüfen, was das andere Programm so als Encoding akzeptiert.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Gremlin hat geschrieben:Im String ist ein ä, ich encodiere ihn mit utf-8, verbinde ihn mit weiteren utf-8 strings und starte das programm.
Doch das Programm verweigert den Start, weil das ä kein ä ist, sondern \xc3\xa4.

Ist wohl nurn Verständnisproblem, blicke zwar inzwischen "ganz grob" durch bei diesem Thema, aber scheinbar halt doch noch nicht so perfekt.
Dein erstes Verständnisproblem ist, dass du die Fehlermeldung und Code nicht postet.
- Das "ä" ist der Char(akter) bzw. Glyph zur grafischen Darstellung, also dem was "ich" sehe.
Ja.
Das utf-8 Code-Point Äquivalent \xc3\xa4 ist das was python "sieht" und auch weitergibt wenn ich mit subprocess das Programm starte, richtig?
Nein. "\xc3\xa4" ist ein Bytestring der Länge zwei, mehr nicht und darin liegt auch das Problem, weil du damit anscheinend irgendwas machen willst, wofür er nicht gemacht ist. Aber hier fehlen Fehlermeldung und Code.
- Angenommen es ist richtig, wie sag ich dann python es soll nicht den Code-Point sondern den Charakter mit subprocess verwenden?
Indem du den String nicht vorher in utf8 codierst bzw. ihn im anderen Programm wieder dekodierst. Aber darüber zu spekulieren was du machen musst ist müßig solange du die Fehlermeldung + Beispielcode nicht postest.
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Hyperion hat geschrieben:Weißt Du denn, welches Encoding das externe Programm erwartet? Vermutlich kann es eben nicht mit uft-8 umgehen...
Das war der Knackpunkt. Hab bisher nur in die Richtung gedacht was mein Programm erwartet :roll: (Das externe will iso-8859-1)
Hyperion hat geschrieben:Generell solltest Du intern solange wie möglich mit unicode arbeiten und nicht mit Byte-Strings, wie Du es beschrieben hast. Konkateniere alles in Unicode und wandle erst ganz am Schluss in ein passendes Encoding.
Dann ergibt sich für mich da aber wieder ne Frage, und zwar wie soll ich das machen? Sehr viel in meinem Programmcode besteht aus konstanten strings und die hab ich mit einem cookie als utf-8 ausgewiesen. Kann ich die auch per cookie als unicode ausweisen oder muss ich dann wirklich überall das "u" vorne dran hängen? (Mein Gefühl sagt mir zwar, ja, muss ich, aber fragen kostet ja nichts..)
Darii hat geschrieben:Dein erstes Verständnisproblem ist, dass du die Fehlermeldung und Code nicht postet.
Naja, bei folgender Fehlermeldung (Das Programm ist übrigens 7zip) kann man (ich?) nicht viel verstehen: (Code 2) Fatal Error
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Gremlin hat geschrieben: Dann ergibt sich für mich da aber wieder ne Frage, und zwar wie soll ich das machen? Sehr viel in meinem Programmcode besteht aus konstanten strings und die hab ich mit einem cookie als utf-8 ausgewiesen. Kann ich die auch per cookie als unicode ausweisen oder muss ich dann wirklich überall das "u" vorne dran hängen? (Mein Gefühl sagt mir zwar, ja, muss ich, aber fragen kostet ja nichts..)
Naja, im Quellcode steht ja nun einmal kein Unicode, sondern irgend welche Byte-Strings. (Wie schön aus den Folien von Leonidas hervorgeht und was auch ich erst mal begreifen musste ist ja grad der Witz, dass Unicode nur ein Konzept und keine Codierung zum Speichern darstellt.) Python muss nun ja wissen, mit welchem Encoding diese Strings im Programm stehen, damit der Interpreter das gante parsen kann. Standardmäßig wird bei Python 2.x ASCII angenommen; will man etwas anderes hilft das Festlegen des Encodings (es muss natürlich dann auch in dem Format gespeichert sein ;-) ).
Unicode-Strings sind in Deiner Datei ja zunächst eben auch Bytestrings; damit Python diese automatisch decodieren kann, braucht es ebenfalls die Angabe des Encodings oben im Script Kopf. Somit kann der Interpreter aus den Bytestrings Deiner Code-Datei intern in Unicode wandeln.

Die simple Antwort lautet damit: Durch Festlegen des Encodings im Dateikopf, kannst Du eben Unicode-Strings in den Quellcode hinschreiben, die Umlaute usw. enthalten können, sofern das Endoding der Datei dieses zulässt. Python nimmt dann intern eben dieses Encoding und es klappt.

Kannst Du ja mal testen:

Code: Alles auswählen

#!/usr/bin/python

s = u"Hallöle"
Das kannst Du ruhig als utf-8 speichern - Python wird dennoch ASCII als default encoding annehmen und dann intern den Unicode-String (der hier ja eben noch ein Bytestring ist) mit ASCII als encoding decodieren. Damit läuft der Interpreter in einen Fehler.

Schreibst Du das Encoding rein, klappt es :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Gremlin hat geschrieben:Naja, bei folgender Fehlermeldung (Das Programm ist übrigens 7zip) kann man (ich?) nicht viel verstehen: (Code 2) Fatal Error
Wenn dir die Fehlermeldung nichts sagt, solltest du sie (+Code) erst Recht posten. Das verhindert nämlich unnötige Spekulationen seitens derjenigen die dir helfen wollen.
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Danke. So ganz langsam sitzt es, denke ich :roll:

Aber mal so nebenbei, die Folien von Leonidas hab ich mir angesehen, aber wirklich verstanden hab ich nicht viel. Ist ja schließlich in Form einer Präsentation aufgebaut (Stichpunkte, etc.) und um da wirklich durchzusteigen fehlen die dazugehörigen Erläuterungen, finde ich.
Darii hat geschrieben:Wenn dir die Fehlermeldung nichts sagt, solltest du sie (+Code) erst Recht posten. Das verhindert nämlich unnötige Spekulationen seitens derjenigen die dir helfen wollen.
Ich werds mir fürs nächste Mal merken. :)
fhoech
User
Beiträge: 143
Registriert: Montag 9. April 2007, 18:26

Das externe will iso-8859-1
Das ist aber auch nicht das richtige Encoding. Alle an suprocess.Popen/call übergebenen Strings sollten mit sys.getfilesystemencoding() kodiert werden. Unter Windows läuft das aufs Encoding 'mbcs' hinaus, unter Mac OS X (immer) und Linux (meist) auf UTF-8.
Der Grund, dass unter Windows für manche Zeichen auch iso-8859-1 stattdessen funktioniert, ist, dass bestimmte Codepoints überlappen, z.B.:

Code: Alles auswählen

>>> import sys
>>> u'ä'.encode('iso-8859-1')
'\xe4'
>>> u'ä'.encode(sys.getfilesystemencoding())
'\xe4'
Darauf darf man sich aber nicht verlassen:

Code: Alles auswählen

>>> u'\u2022'.encode(sys.getfilesystemencoding())  # \u2022 = • = Aufzählungszeichen
'\x95'
>>> u'\u2022'.encode('iso-8859-1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character u'\u2022' in position 0: ordinal not in range(256)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

fhoech hat geschrieben:
Das externe will iso-8859-1
Das ist aber auch nicht das richtige Encoding.
Woher weißt Du das?
fhoech hat geschrieben: Alle an suprocess.Popen/call übergebenen Strings sollten mit sys.getfilesystemencoding() kodiert werden. Unter Windows läuft das aufs Encoding 'mbcs' hinaus, unter Mac OS X (immer) und Linux (meist) auf UTF-8.
In der Doku steht dazu aber nichts. Gibt es da eine Quelle oder eine Begründung?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
fhoech
User
Beiträge: 143
Registriert: Montag 9. April 2007, 18:26

Hyperion hat geschrieben:
fhoech hat geschrieben:
Das externe will iso-8859-1
Das ist aber auch nicht das richtige Encoding.
Woher weißt Du das?
Aus eigener Erfahrung.
Hyperion hat geschrieben:
fhoech hat geschrieben: Alle an suprocess.Popen/call übergebenen Strings sollten mit sys.getfilesystemencoding() kodiert werden. Unter Windows läuft das aufs Encoding 'mbcs' hinaus, unter Mac OS X (immer) und Linux (meist) auf UTF-8.
In der Doku steht dazu aber nichts. Gibt es da eine Quelle oder eine Begründung?
Leider nein. Ich hätte mir gewünscht, das dazu etwas in der Doku steht, oder das subprocess automatisch eine entsprechende Kodierung von Unicode-Strings vornimmt. Es funktioniert aber so zuverlässig. Um das wirklich technisch zu begründen, müsste man sich wahrscheinlich die entsprechenden Systemaufrufe für Dateinamenoperationen auf den jeweiligen Systemen ansehen. Ich würde mutmaßen, die meisten Programme sind was das angeht wahrscheinlich ziemlich 'dumm', sprich sie nehmen den/die übergebenen Dateiparameter als Bytestring entgegen und überlassen den Umgang damit den Systemaufrufen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

fhoech hat geschrieben:
Hyperion hat geschrieben:
fhoech hat geschrieben: Das ist aber auch nicht das richtige Encoding.
Woher weißt Du das?
Aus eigener Erfahrung.
Dann hast Du eine gute Glaskugel ;-)
fhoech hat geschrieben: Leider nein. Ich hätte mir gewünscht, das dazu etwas in der Doku steht, oder das subprocess automatisch eine entsprechende Kodierung von Unicode-Strings vornimmt. Es funktioniert aber so zuverlässig. Um das wirklich technisch zu begründen, müsste man sich wahrscheinlich die entsprechenden Systemaufrufe für Dateinamenoperationen auf den jeweiligen Systemen ansehen. Ich würde mutmaßen, die meisten Programme sind was das angeht wahrscheinlich ziemlich 'dumm', sprich sie nehmen den/die übergebenen Dateiparameter als Bytestring entgegen und überlassen den Umgang damit den Systemaufrufen.
Ich würde jetzt annehmen, dass das eben von Programm zu Programm unterschiedlich gehandhabt werden wird. Viele werden wohl das OS spezifische Encoding bei ihrer Annahme von externen Parametern annehmen, andere aber vielleicht nicht. Insofern denke ich nicht, dass Deine verallgemeinerte Aussage so Bestand haben kann - ich mag mich da auch irren, aber als PoC könnte man ja eben selber ein Programm schreiben, dass ein spezielle Encoding voraussetzt, welches sich vom OS default unterscheidet. Mir leuchtet es zumindest nicht ein, dass Dein Rezept als absoluter Maßstab gelten kann.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@fhoech: Deine Erfahrung mag so sein, die Schlussfolgerungen die Du daraus ziehst stimmen aber nicht. `subprocess` kann nichts automatisch kodieren, weil eben nicht ermittelt werden kann, in welcher Kodierung die gestartete Anwendung die Kommandozeilenargumente erwartet. Das sind einfach nur Bytes und sowohl Aufrufer als auch Aufgerufener müssen von der selben Kodierung ausgehen. Es ist aber nirgends festgelegt welche das ist.
fhoech
User
Beiträge: 143
Registriert: Montag 9. April 2007, 18:26

Dann hast Du eine gute Glaskugel ;)
Naja, also das kann man durchaus testen (das ist der Weg, den ich gegangen bin). Alles was man dazu braucht, ist das entsprechende Programm und ein paar Dateien mit Namen, die einen gewissen Unicode-Bereich ausserhalb diverser Single-Byte-Encodings abdecken (die kann man ja auch programmatisch erzeugen).
Mir leuchtet es zumindest nicht ein, dass Dein Rezept als absoluter Maßstab gelten kann.
So war es auch nicht gedacht. Aber irgendwie kodiert muss man subprocess ja die Strings übergeben, und ehe man hier ein (auch nur geratenes) Encoding fest vorgibt, kann man sys.getfilesystemencoding() verwenden.
fhoech
User
Beiträge: 143
Registriert: Montag 9. April 2007, 18:26

BlackJack hat geschrieben:@fhoech: Deine Erfahrung mag so sein, die Schlussfolgerungen die Du daraus ziehst stimmen aber nicht.
Ok, ich stehe auf dem Schlauch. Welche Schlussfolgerungen?
BlackJack hat geschrieben:`subprocess` kann nichts automatisch kodieren, weil eben nicht ermittelt werden kann, in welcher Kodierung die gestartete Anwendung die Kommandozeilenargumente erwartet. Das sind einfach nur Bytes und sowohl Aufrufer als auch Aufgerufener müssen von der selben Kodierung ausgehen.
Das bestreite ich ja auch nicht, bzw. habe nichts anderes behauptet. Ich wollte einfach einen Weg aufzeigen, der zuverlässig funktioniert (für die gegebene Anwendung, 7zip, aber eben meiner Erfahrung nach auch für andere).
Blackjack hat geschrieben:Es ist aber nirgends festgelegt welche das ist.
Leider. Wäre das irgendwo dokumentiert, hätte ich mir einen Haufen zeitaufändige Tests sparen können (damals, als ich das erste Mal damit konfrontiert wurde ;)).
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

fhoech hat geschrieben:
Dann hast Du eine gute Glaskugel ;)
Naja, also das kann man durchaus testen (das ist der Weg, den ich gegangen bin). Alles was man dazu braucht, ist das entsprechende Programm und ein paar Dateien mit Namen, die einen gewissen Unicode-Bereich ausserhalb diverser Single-Byte-Encodings abdecken (die kann man ja auch programmatisch erzeugen).
Es ging aber darum, dass Du dem OP widersprochen hast, dass das "iso-8859-1" Encoding hier das erwartete ist. Und da der OP das Programm nicht genannt hat, dürfte das eben reines "raten" sein ;-)

Edit: oops. Ok, sehe ich jetzt erst, dass er es doch genannt hat. Also magst Du hier doch recht haben :-)
So war es auch nicht gedacht. Aber irgendwie kodiert muss man subprocess ja die Strings übergeben, und ehe man hier ein (auch nur geratenes) Encoding fest vorgibt, kann man sys.getfilesystemencoding() verwenden.
Ok, so formuliert klingt es schon ganz anders. Auch das ist ja eben nur "raten", wenn vielleicht auch ein ganz "gutes". Jedoch kannst Du dem OP ja nicht unterstellen, dass er geraten hat. Evtl. stand ja in der Doku zum speziellen Programm, was es erwartet ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
fhoech
User
Beiträge: 143
Registriert: Montag 9. April 2007, 18:26

Auch das ist ja eben nur "raten", wenn vielleicht auch ein ganz "gutes".
Ist mir bewusst. Deshalb schrieb ich ja auch "auch" ;)
Jedoch kannst Du dem OP ja nicht unterstellen, dass er geraten hat. Evtl. stand ja in der Doku zum speziellen Programm, was es erwartet
Das war ja jetzt keine "böswillige" Unterstellung (bzw. nicht so gemeint), und ich gebe zu, ich habe nicht extra in die 7zip-Doku geschaut. Aber Tatsache ist, das subprocess bei meinem Beispieldateinamen (mit u"\u2022") bei "iso-8859-1" einen Fehler wirft, daher meine Annahme (mit 'mbcs' oder eben sys.getfilesystemencoding funktioniert es).
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Hm, ich hab aber geraten, naja gut, bevor ich diese Behauptung aufgestellt habe hab ichs schon getestet, aber beweisen könnt ichs trotzdem nicht :lol:
Zuletzt geändert von Gremlin am Montag 23. August 2010, 08:53, insgesamt 1-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

fhoech hat geschrieben:Aber Tatsache ist, das subprocess bei meinem Beispieldateinamen (mit u"\u2022") bei "iso-8859-1" einen Fehler wirft, daher meine Annahme (mit 'mbcs' oder eben sys.getfilesystemencoding funktioniert es).
Naja, vielleicht liegt es auch daran, dass ISO 8859-1 einfach kein "U+2022 BULLET" kennt. Warum sollte es auch?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
fhoech
User
Beiträge: 143
Registriert: Montag 9. April 2007, 18:26

Leonidas hat geschrieben:
fhoech hat geschrieben:Aber Tatsache ist, das subprocess bei meinem Beispieldateinamen (mit u"\u2022") bei "iso-8859-1" einen Fehler wirft, daher meine Annahme (mit 'mbcs' oder eben sys.getfilesystemencoding funktioniert es).
Naja, vielleicht liegt es auch daran, dass ISO 8859-1 einfach kein "U+2022 BULLET" kennt. Warum sollte es auch?
Klar, das ist ja gerade der Knackpunkt :)

Edit: Ups, da hab ich ja tatsächlich Blödsinn geschrieben, sorry. Hast natürlich recht, nicht subprocess wirft den Fehler, sondern natürlich die encode()-Methode. Hab mich da nicht gut ausgedrückt. Worauf ich eigentlich hinaus wollte, ist, dass Dateinamen Zeichen enthalten können, die nicht in (z.B.) ISO 8859-1 vorkommen.
Antworten