Darstellung deutscher Sonderzeichen in Konsole

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.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@kodela: hättest Du tatsächlich eine UTF-8 kodierte Datei, würde das hier erscheiben:

Code: Alles auswählen

c:\TEMP>python x.py
Wer ├ûl f├╝r ein b├Âses ├£bel h├ñlt, bekommt gro├ƒen ├ärger.
Standardmäßig ist für die CMD-Shell CP850 eingestellt. Würdest Du in Deinem Editor tatsächlich dieses Encoding einstellen, würdest Du das erhalten:

Code: Alles auswählen

c:\TEMP>python x.py
Wer Öl für ein böses Übel hält, bekommt großen Ärger.
Nur wenn Du im Editor CP1252 einstellst, bekommst Du das von Dir gezeigte Bild:

Code: Alles auswählen

c:\TEMP>python x.py
Wer Íl f³r ein b÷ses ▄bel hõlt, bekommt gro▀en ─rger.
Die Lösung ist, einen Unicode-String per print auszugeben. Dann wird automatisch ins Ausgabe-Encoding der shell konvertiert:

Code: Alles auswählen

# -*- coding: utf-8 -*-
print u'Wer Öl für ein böses Übel hält, bekommt großen Ärger.'
ergibt:

Code: Alles auswählen

c:\TEMP>python x.py
Wer Öl für ein böses Übel hält, bekommt großen Ärger.
Komischerweise prüft python nicht, ob bei '# -*- coding: utf-8 -*-' die Datei tatsächlich utf-8-kodiert ist, so dass es völlig egal ist, was da steht, solange man keine Unicode-Strings benutzt.
BlackJack

Wobei die Lösung mit den Unicode-Literalen bzw. generell Unicode-Objekte per ``print`` ausgeben nur so lange eine Lösung ist, wie es Python möglich ist die erwartete Kodierung (richtig) zu raten. Wenn das letzte Beispiel ``c:\TEMP>python x.py > output.txt`` oder ``c:\TEMP>python x.py | more`` lauten würde, bekäme man eine Ausnahme statt der Ausgabe.
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

@Sirius3:

Danke für Dein Hilfe, aber ... siehe selbst:

Bild
und auch:

Bild
Ich habe die explizite Deklaration eines Unicode-Strings auch schon versucht.

Auch

Code: Alles auswählen

teststring = u'Wer Öl für ein böses Übel hält, bekommt großen Ärger.'
bringt keinen Erfolg.

MfG, kodela
BlackJack

@kodela: Der Kodierungsfehler kommt weil oben in der Datei offenbar im Coding-Kommentar UTF8 steht, das aber nicht stimmt. Wurde ja schon mal gesagt: In dem Kommentar muss die Kodierung angegeben werden in der die Datei auch tatsächlich kodiert ist.
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo @BlackJack:

Danke! Langsam bin ich am Durchdrehen. Ok, UTF-8 stimmt nicht, aber was zum Teufel stimmt dann? Wie kann ich das herausfinden?

Wenn ich den Coding-Kommentar entferne, bekomme ich ja auch einen Fehler:

"SyntaxError: Non-ASCII character '\xd6' in file D:\Python\Projekte\Testprojekt\src\testprojekt.py on line 9, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details"

MfG, kodela
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@kodela:
Für deutsche Windowsinstallation nehmen Editoren idR cp1252 (Windows-1252) als Voreinstellung. Heisst wenn Du einen Editor aufmachst und nicht dessen Voreinstellung dauerhaft umgestellt hast, sollte er Dateien mit cp1252 kodieren. Dann MUSS der "coding"-Kommentar darauf zeigen, sonst sieht Python da alles mögliche nur nicht Deine Umlaute. Damit hättest Du das Editor-Encoding --> Pythonsource-Encoding Problem gelöst.

Für das Output-Encoding musst Du das ggf. separat selbst lösen:

Bytestrings sollte man zunächst in unicode überführen (generell immer eine gute Idee, da man pythonintern viel besser mit Unicode hantieren kann):

Code: Alles auswählen

'äöüß'.decode('cp1252')
oder bei literaler Eingabe gleich das u'' voranstellen:

Code: Alles auswählen

# coding: cp1252

u'äöüß'
# ist in Python 2.x  äquivalent zu
'äöüß'.decode('cp1252')
Beim eigentlichen Print-Output kann es passieren, dass das Python entweder das Encoding nicht erkannt hat (zu prüfen mit `sys.stdout.encoding`), ein falsches gesetzt ist oder das unterstützte Encoding bestimmte Zeichen nicht beherrscht. Letzteres führt zum `UnicodeEncodeError`, da hilft nur entweder die Zeichen zu ersetzen/verwerfen (siehe https://docs.python.org/2/howto/unicode ... icode-type) oder wenn man Glück hat, unterstützt das Device irgendein Unicode-Encoding. Dann nimmt man dieses für den Output:

Code: Alles auswählen

print u'äöü߀'.encode('utf-??')
Die Window-Konsole kann prinziell Unicode, muss dafür aber explizit umgestellt werden (für höhere Unicode-Zeichen muss dann zusätzlich noch eine Unicode-Schrift eingestellt sein, sonst fehlen die Glyphen).
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo @jerch:

Danke für Deine Hinweise. Leider bringen auch die mich nicht weiter.

Für den Editor der NetBeans-IDE ist Windows-1252 defaultmäßig für das Encoding eingesetzt und das belasse ich auch so. Da Durch meine vielen Testläufe ja einiges durcheinander gekommen sein könnte, habe ich den Text (zum wiederholten Male) neu eingetippt. Für den Coding-Kommentar habe ich ebenfalls cp1252, das ja Windows 1552 entspricht, eingestellt. Mit der Darstellung im Editor gibt es damit keinerlei Probleme.

Die beiden von Dir vorgeschlagenen Varianten der Überführung des Bytestring nach unicode funktionieren bei mir leider nicht. Für

teststring = u'Wer Öl für ein großes Übel hält, bekommt bösen Ärger' (Zeile 6)

bekomme ich folgende Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "D:\Python\Projekte\Testprojekt\src\testprojekt.py", line 13, in <module>
    main()
  File "D:\Python\Projekte\Testprojekt\src\testprojekt.py", line 9, in main
    print teststring
UnicodeEncodeError: 'ascii' codec can't encode character u'\xd6' in position 4: ordinal not in range(128)
In der Zeile 9 steht die print-Ausgabe (print teststring)

Exakt die selbe Fehlermeldung bekomme ich bei einer Transkodierung mit

teststring = 'Wer Öl für ein großes Übel hält, bekommt bösen Ärger'.decode('cp1252') (Zeile 6)

Frage ich anschließend mit sys.stdout.encoding das Encoding ab, bekomme ich als Ergebnis None.

Wenn ich diese Fehlermeldungen ignoriere und den Code über die Konsole ausgebe, werden die Zeichen korrekt dargestellt. Damit kann man leben. Nochmals vielen Dank für Deine Hinweise.

MfG, kodela
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@kodela:
Probier mal in der Konsole:

Code: Alles auswählen

#> chcp 1252
#> python <dein programm>
Mit

Code: Alles auswählen

import sys
sys.setdefaultencoding('cp-1252')
kannst Du dann noch versuchen, Python das Encoding für STDOUT klar zu machen. Dein UnicodeEncodeError deutet darauf hin, dass print auf ASCII zurückfällt, weil es das Encoding nicht hat. Und Ö ist nicht im ASCII-Bereich, sondern 0xD6 sowohl in cp1252 als auch in Unicode.
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo @jerch:

das habe ich gemacht:

Code: Alles auswählen

D:\Python\Projekte\Testprojekt\src>chcp 1252
Aktive Codepage: 1252.

D:\Python\Projekte\Testprojekt\src>python
Python 2.7.6 (default, Oct 21 2015, 09:27:24) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.setdefaultencoding('cp-1252')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'setdefaultencoding'
>>>
und das ist das Ergebnis:

Code: Alles auswählen

Wer Íl f³r ein gro▀es ▄bel hõlt, bekommt b÷sen ─rger
Trotzdem herzlichen Dank

MfG, kodela
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Was den AttributeError betrifft, ist die Dokumentation ist da ziemlich klar:

sys.setdefaultencoding(name)

Set the current default string encoding used by the Unicode implementation. If name does not match any available encoding, LookupError is raised. This function is only intended to be used by the site module implementation and, where needed, by sitecustomize. Once used by the site module, it is removed from the sys module’s namespace.
In specifications, Murphy's Law supersedes Ohm's.
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo allen,

die Ursache ist gefunden und das Problem behoben.

Aus welchen Gründen auch immer war bei mir die Codepage 850 eingestellt. Für die Darstellung der Sonderzeichen sollte aber die Codepage 1252 eingestellt sein.

Die Umstellung von der Standard-Codepage 850 erfolgt in der Registry unter

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage

Dort ist der Eintrag für den Wert OEMCP von 850 auf 1252 zu ändern. Die Änderung wird erst nach einem Neustart wirksam.

Ich bedanke mich bei allen, die mir bei der Problemsuche behilflich waren, sehr herzlich.

MfG, kodela
bb1898
User
Beiträge: 200
Registriert: Mittwoch 12. Juli 2006, 14:28

kodela hat geschrieben: Aus welchen Gründen auch immer war bei mir die Codepage 850 eingestellt.
Dass das unter Windows das Normale ist, hat Dir Sirius3 schon im ersten Beitrag auf dieser Seite gesagt. Es scheint nur auch bei den Antwortenden unter den Tisch gefallen zu sein.
Für die Darstellung der Sonderzeichen sollte aber die Codepage 1252 eingestellt sein.
Nein, cp850 kann Umlaute genauso, man muss Python nur sagen, dass die Ausgabe so erfolgen soll. Also (ungetestet mangels Python 2):

Code: Alles auswählen

teststring = u"äüöß"
print teststring.encode('cp850')
Die Umstellung von der Standard-Codepage 850 erfolgt in der Registry unter

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage

Dort ist der Eintrag für den Wert OEMCP von 850 auf 1252 zu ändern. Die Änderung wird erst nach einem Neustart wirksam.
Nicht nötig. Hilfreicher ist ein Umsteigen auf Python 3. Das schreibt Dir mit "print(teststring)" den String mit korrekten Umlauten in die Windows-Konsole, ohne dass Du irgendwo hin- oder hercodierst.
BlackJack

@bb1898: Was macht Python 3 denn anders als Python 2 wenn man dort eine Unicode-Zeichenkette ausgibt? Auch Python 2 kodiert das automagisch.

Edit: Python-Quelltext:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8

print u'Noch mehr Öl ins Feuer. :-)'
Ausgabe:

Code: Alles auswählen

H:\>python forum4.py
Noch mehr Öl ins Feuer. :-)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@bb1898:
Das Problem im Threadverlauf war, dass kodelas Dateiencoding unklar war bzw. nicht stimmen konnte. Warum er die Konsolenausgabe jetzt per Umstellung auf cp-1252 "gelöst" hat - kA. Solange Python die richtigen Encodings von Datei bis Ausgabe hat, sollte das auch unter 2.x klappen.
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo @bb1898:

Danke für Deine Hinweise. Besonders neu sind sie allerdings nicht. Dass bei Windows Vista und 7 die Codepage 850 ein quasi Standard ist, hat nicht nur Sirius3 festgestellt, auch ich habe geschrieben, dass die Umstellung von der Standard-Codepage 850 in der Registry erfolge.

Standard ist aber eine Sache, die Realität eine andere. Wenn man zum Beispiel unter Google den Suchbegriff "standard für windows codepage" eingibt, bekommt man an erster Stelle diesen Verweis: Windows-1252 - Wikipedia. Auch bei den nachfolgenden Verweisen findet man noch lange keine Hinweise auf die Codepage 850.

Nach meinen Erfahrungen werden im deutschen Sprachraum Computer mit vorinstalliertem Windows in den allermeisten Fällen mit der Codepage Windows-1252 ausgeliefert. Auf zwei anderen Rechnern von mir ist diese Codepage zum Beispiel eingestellt, ohne dass ich dies explizit gemacht hätte.

Auch die beiden von Dir vorgeschlagenen Varianten, den Ausgabestring entweder mit encode('cp850') oder dem vorangestelltem Literal "u" an die Einstellung unter Windows anzupassen, wurden hier bereits angesprochen und als gangbare Wege festgestellt. Nur ist es mir aber lieber, wenn ein Programm nicht ganz gezielt an eine Umgebung angepasst, sondern so gestaltet ist, dass es nach Möglichkeit unter allen Umgebungen funktioniert.

Neu ist also lediglich Dein Vorschlag, von Python 2 auf Python 3 umzusteigen. Nun, wegen einem Miniproblem, das sich durch eine simple Änderung in der Registry beheben lässt, auf Python 3 umzusteigen, nein, das sehe ich nicht als einen guten Vorschlag an.

MfG, kodela
BlackJack

@kodela: Die Konsole verwendet in der Regel nicht die Standard-*Windows*-Codepage sondern die Standard-*DOS*-Codepage für die jeweilige Region die im System eingestellt ist. Und cp850 ist für Deutschland die übliche DOS-Codepage. Macht ja auch Sinn, denn diese Konsole ist ja ein Überbleibsel aus DOS-Zeiten und ermöglicht(e) die Ausführung von DOS-Programmen und die gehen in der Regel von der DOS-Codepage aus.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

kodela hat geschrieben:Auch die beiden von Dir vorgeschlagenen Varianten, den Ausgabestring entweder mit encode('cp850') oder dem vorangestelltem Literal "u" an die Einstellung unter Windows anzupassen, wurden hier bereits angesprochen und als gangbare Wege festgestellt. Nur ist es mir aber lieber, wenn ein Programm nicht ganz gezielt an eine Umgebung angepasst, sondern so gestaltet ist, dass es nach Möglichkeit unter allen Umgebungen funktioniert.
Kleine Anmerkung dazu - wenn Du es plattform unabhängig halten willst, fährst Du mit der Kombination aus richtigem Coding-Kommentar + u''-Strings unter Python 2.x am besten. Dann transkodiert Python beim Laden des Sourcescodes literale Strings automagisch zu Unicode, womit sich in 95% der Fälle eh besser arbeiten lässt. (Ist ein Grund, warum in Python 3 literale Strings nicht mehr als Bytestrings verarbeitet werden.)
kodela
User
Beiträge: 185
Registriert: Montag 12. Oktober 2015, 21:24
Wohnort: Landsberg am Lech
Kontaktdaten:

Hallo @jerch:

Danke für den Hinweis. Ja Du hast vermutlich Recht. Man könnte für die Ausgabe bestimmte Strings auch noch nach Unicode transcodieren.

MfG, kodela
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@kodela:
Der interne Unicodetyp von Python ist nicht für die direkte (Ein-) Ausgabe gedacht, er hat nicht einmal eine einheitliche Byte-Repräsentation (kann 2 oder 4 Bytes lang sein, je nach OS und Pythonversion). Der Unicodetyp ist für die interne Verarbeitung optimiert, für IO mit dem Betriebssystem muss eigentlich immer transkodiert werden (Dekodierung zu Unicode bei Eingabe, Enkodierung von Unicode zu Zielkodierung bei Ausgabe):

Eingabe --> Dekodierung - Unicode - Kodierung --> Ausgabe

Python versucht nun dieses umständliche Hin- und Herkodieren etwas zu verstecken. So macht der Interpreter z.B. Annahmen über die Kodierung von Standard-IO-Kanälen wie STDIN und STDOUT. Sind diese Annahmen richtig und alle Zeichen im Ausgabe-Encoding darstellbar, kann man z.B. am Interpreter-Prompt eines Terminals einen Unicodestring direkt ausgeben lassen. Python kodiert dabei den Unicodestring im Hintergrund ins Zielformat. Eine Eingabe am Prompt verläuft umgekehrt. Analog wird in Quelldateien ein literaler Unicodestring entsprechend des coding-Kommentars dekodiert.
kodela hat geschrieben:Man könnte für die Ausgabe bestimmte Strings auch noch nach Unicode transcodieren.
Lange Rede kurzer Sinn - solange Python mit seiner Annahme über die korrekte Ausgabe-Kodierung besser Bescheid weiss als Du, stimmt das. Ansonsten nicht.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

Um hier mal den Wildwuchs an falschen Tipps und herumgerate einzudämmen ein paar Fakten:
1. Python kann auf Konsolen Unicode-Strings im richtigen Encoding ausgeben, egal ob Python 2 oder 3. Da braucht man nichts in seiner Registry herumzuspielen oder von Hand irgendwelche Encoding-Magie anwenden. Python weiß normalerweise besser, welches Encoding zu benutzen ist, als irgendein User.
2. das verwendete Encoding steht in sys.stdout.encoding. Wer mit sys.getdefaultencoding oder sys.setdefaultencoding herumspielt macht definitiv etwas falsch.
3. Python 2 kann neben Unicodestrings auch Byte-Strings ausgeben, dann ohne Gewähr, dass das Encoding stimmt. Bei Python3 ist man dem eingestellten Encoding ausgeliefert.
3. Jetzt wird es kompliziert: Bei Dateiumleitungen verhalten sich Python2 und Python3 unterschiedlich: Python2 setzt sys.stdout.encoding auf None, so dass man nur noch ByteStrings oder ASCII ausgeben kann. Python3 rät irgendein Encoding, das es dann benutzt. In Python2 ist es also ganz einfach, eine Dateiumleitung zu erkennen und dann ein gewünschtes Encoding einzustellen:

Code: Alles auswählen

import sys
import codecs

if sys.stdout.encoding is None:
    sys.stdout = codecs.getwriter('utf8')(sys.stdout)

print u"Tätüt trarö"
Will man das Python3-Verhalten möglichst exakt nachbilden, bietet sich diese Lösung an:

Code: Alles auswählen

import sys
import codecs
import locale

if sys.stdout.encoding is None:
    sys.stdout = codecs.getwriter(locale.getpreferredencoding() or 'utf8')(sys.stdout)
mit dem Nachteil, dass, wenn man tatsächlich in eine Datei umleitet, sich das Encoding je nach Systemeinstellung ändern kann.
Antworten