Oh gott .. Zeichensatzprobleme ..
Also:
Wenn man Probleme mit Zeichensatz hat, ist es immer das Beste, ganz genau bei jeder Variable, jeder Eingabe und jeder Ausgabe sich genau zu überlegen, welches Format / welcher Typ das ist, und wo was von wem wie konvertiert wird, und ganz besonders wann eben nicht konvertiet wird. Wenn man da nicht genau ist und zu vermischen anfängt, hat man verloren. Wenn man genau ist, ist es eigentlich ganz leicht. Quasi wie physikalische Einheiten.
Das
Datei-Encoding definiert in der Tat
nicht, in welchem Encoding Strings
ausgegeben werden.
Das Datei-Encoding definiert, wie die individuellen Bytes in der Sourcecode-Datei interpretiert werden sollen, insbesonders wie String Literale gelesen und konvertiert werden.
https://docs.python.org/2/reference/lex ... ml#strings
Erklärung:
Python 2.x kennt zwei Arten von Strings: 'normale' Strings und Unicode Strings.
-
Strings sind in Wahrheit einfache Byte arrays (8-bit characters). Strings haben kein bestimmtes Encoding, man muss das Encoding vom String selber wissen. Standardmäßig werden Strings als ASCII interpretiert. Byte-Werte 1-127 sind gültige ASCII Zeichen, 128-255 sind keine gültigen Zeichen, können aber verwendet werden. Ungültige Zeichen werden als Hex-Code angezeigt (z.b. \xe4 für Latin-1 'ä', bzw. \xc3\xa4 für UTF-8 'ä').
-
String Literale sind String Konstanten im Quellcode, mit Anführungszeichen geschrieben.
Das Datei-Encoding definiert, wie Zeichen in String-Literalen vom Editor angezeigt werden bzw wie eingegebene Zeichen in der Source-Datei und somit in der String Variable als Bytes gespeichert werden.. Mit Escape-Sequenzen kann man auch Zeichen außerhalb des Encodings eingeben (z.b. s = "abc\xe4" für "abcä"; Man bedenke dass die Escape-Sequenz natürlich im Encoding der Datei eingegeben werden muss... aber diese Zeichen sind ASCII und haben in fast allen Encodings die selbe Binär-Repräsentation).
Wenn man z.B. s = "abcä" hinschreibt, landet dann im String die Byte Werte 0x61, 0x62, 0x63, 0xE4 - vorausgesetzt das Encoding der Datei ist Latin-1! Wenn man die selbe Datei als ASCII oder UTF-8 definieren würde, wäre der Text einfach ungültig und würde kaputt angezeigt werden, bzw. kann Python bei UTF-8 sogar das schließende " nicht finden und die Quelldatei nicht mal
lexen (und somit schon gar nicht parsen ..

).
Wenn man s = "abcä" in einer UTF-8 Datei hinschreibt, landen die Byte Werte 0x61, 0x62, 0x63, 0xC3, 0xA4.. D.h.
beim dekodieren des Strings nach unicode() muss man das Encoding der Quellcode-Datei kennen!
-
Unicode Strings sind String Variablen die nur gültige Unicode Zeichen (Codepoints) beinhalten. Der Inhalt ist *immer* Unicode, ungültige Unicode-Codepoints sind nicht erlaubt. Das Encoding ist nur relevant, wenn man den Unicode String wo einließt oder rausschreibt (also als Byte-Sequenz in einem bestimmten Format wohin schreibt oder woher bekommt).
-
Unicode String Literale sind String Konstanten die nur Unicode beinhalten.
Das Datei-Encoding definiert, wie Zeichen in Unicode-Literalen interpretiert und in codepoints umgewandelt werden. Wenn man z.B. s = u"abcä" schreibt (bei Latin-1 Datei Encoding) wird in s ein Unicode String mit den Codepoints für die Zeichen 'a', 'b', 'c' und 'ä' gespeichert.
Da der erzeugte String ein Unicode-String ist, ist der Inhalt des Strings unabhängig vom Encoding der Datei!
https://docs.python.org/2/howto/unicode.html
Python erkennt außerdem das Encoding von stdout und stdin automatisch. Wenn man z.B. Python auf der Windows-Shell ausführt, ist das Output Encoding UTF-8 oder CP-1252. Wenn man die Ausgabe oder Eingabe in eine Datei oder eine Pipe umleitet, hat Python kein Encoding. Print konvertiert Unicode Strings automatisch auf das richtige Output Encoding. Normale Strings werden (denke ich) als ASCII interpretiert und entweder 1:1 als bytes direkt oder mit Hex Escape-Sequenzen für Non-ASCII chars ausgegeben.
c:\> python
import sys
print(sys.stdin.encoding, sys.stdout.encoding)
Daher:
- Bei
Quellcode Dateien prinzipiell *immer*
nur ASCII verwenden und
keine Sonderzeichen im Quellcode verwenden. Wenns aber sein muss und man unbedingt Umlaute in String Literalen verwenden muss ohne sie in Hex einzutippen, dann
UTF-8 als Datei-Encoding verwenden. Dann aber jene String Literale die Umlaute,.. beinhalten als
Unicode String Literale definieren (u"abcä.."); Kommentare und Variabennamen,.. sollten sowieso immer Englisch sein und nie Sonderzeichen beinhalten.
Niemals Latin-1/8859-1 oder so was als Zeichensatz verwenden (UTF-8 Text-Dateien werden (in der Regel) mit einem Magic Byte als solche erkennbar gemacht, bei Latin-1 etc. muss der Editor/der Programmierer raten; Bei Programmiersprachen wie C etwas anderes als ASCII verwenden ist prinzipiell eine schlechte Idee).
BTW, besonders wenn man plattformübergreifend arbeitet (aber am besten generell) ist es auch ratsam, sich einheitliche Line-Endings in allen Dateien anzugewöhnen, am besten Unix-Lineendings (LF) - auch wenn Notepad denk ich immer noch nicht damit zurecht kommt. Sonst bekommt man Probleme mit dem Versionssystem (Git) und anderen Tools.
-
Niemals Sonderzeichen in normalen String-Literalen verwenden, sondern Unicode String-Literale verwenden.
- Text der Sonderzeichen beinhalten können sollte als Unicode String behandelt werden. Dann kümmert sich print selbst um das richtige Encoding bei der Ausgabe.
-
Wenn man Strings von irgendwo als nicht-Unicode Strings reinbekommt, muss man sie erst mit str().decode() mit dem Input-Charset dekodieren und in Unicode umwandeln. Wenn man Unicode Strings in eine Datei schreiben oder über den seriellen Port schicken will, muss man sie erst ins richtige Format kodieren (unicode().encode()).
Was also über den Serial-Port daherkommt ist sicher ein reiner Byte String, der in irgendeinem Encoding reinkommt (Latin-1 vermutlich). Den musst du dekodieren um ein Unicode Objekt draus zu machen, dann wirds beim print automatisch aufs richtige Terminal-Charset konvertiert.
# Returns an latin-1/.. encoded byte-array
input = read_from_serial()
# Decode with the proper encoding that is used by the sender
unicode_input = input.decode("latin-1")
# Let print deal with the correct output encoding
print( unicode_input )
# Encode as UTF-8 byte-array, e.g. for writing to a file
utf8_output = unicode_input.encode("utf-8")
Python 3:
Bei Python 3 hat sich das Verhalten und die Syntax quasi komplett umgedreht:
http://pythoncentral.io/encoding-and-de ... ython-3-x/
- In Python 3.x sind
alle Strings Unicode. Das Datei-Encoding bestimmt weiterhin, wie die gespeicherten Bytes im Quellcode nach Unicode umgewandelt wird und im Editor dargestellt werden.
- Die "normalen" 8-bit strings von Python 2.x sind in Python 3.x byte arrays (b"abc\xe4"). Sonderzeichen in Byte-Arrays können nicht direkt als Literale eingegeben werden (b"abcä" ist ein Fehler).
- Konvertieren von byte arrays zu (unicode) Strings geht weiterhin über .decode(), allerdings funktioniert dies nur für echte Byte-arrays, nicht für Strings (also "abcä".decode("latin-1") geht nicht, nur
b"abc\xe4".decode("latin-1") geht).
D.h. bei Python 3.x muss der Serial-Driver statt einem String ein Byte-array zurückliefern (das ist auch in jedem Fall bei Python 2 das richtige, da der serielle Treiber ja nur Bytes auf der Leitung sieht und nicht als darstellbaren Text nach Zeichensatz interpretiert). Dann funktioniert der selbe Code wie oben.
Falls der Python 3 Treiber Strings zurückliefern würde/sollte, muss man ihm das Encoding sagen können müssen, damit er die Strings erst korrekt dekodieren kann. Dann könnte der Treiber allerdings keine Binärdaten übertragen (welche auch Bytes beinhalten kann, die keine legalen Zeichen in Input-Encoding sind - ein Unicode String kann keine beliebigen Binärdaten enthalten).
Für Python 3.x Kompatibilität daher immer Binär-Strings und -Literale als solche explizit erzeugen und dort keine Sonderzeichen verwenden (b"abc\xe4").
I find your lack of platform support disturbing.