Ich blick es leider nicht. Encode/decode/utf8/Umlaute

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.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Ok, ich habe einen Fehler gefunden der definitiv durch mich verschuldet ist! Ein wirklich großes Sorry.
Bin aber erst darauf gekommen als @rmp das mit den Zeichen welche in Textdateien geschrieben werden unter Windows und der Gleichen erklärt hat.

Um nicht ständig die Seite vom Webserver neu laden zu müssen (also bei jedem 'Testdurchgang') habe ich beschlossen die HTML-Seite in eine 'offline'-HTML-Datei zu speichern und diese als Quelle zu nutzen....
Da ist wohl dann der Zeichensatz durcheinander gekommen und ich habe statt München eben Mónchen bekommen (wenn es denn umkodiert wäre. Was ich bekommen habe ist dann eben den Unicode `u'M\03ccnchen'` und jetzt bekomme ich den Unicode `u'M\xfcnchen'`. BlackJack hat es natürlich gleich gemerkt! Also allergrößten Respekt von mir!).

So, nun haben wir damit herausgefunden das die UTF-8-Angabe ja wohl richtig vom Server definiert und verwendet wird, oder sehe ich das falsch?
Wenn das richtig ist: dann muss ich jetzt lediglich 'encode(charset)' anwenden, richtig? Also Unicode (`u'M\xfcnchen'`) ->für mich lesbares Zeichenkette ('München').
Das mache ich dann mit 'Element der Liste'.enode(charset) ?
In meinem Fall dann:

Code: Alles auswählen

print stadt.enode("ISO 8859-1")
Oder UTF8? UTF8 kann die Umlaute ja auch korrekt anzeigen?
Ich bin zwar einierseits neu verwirrt, aber andererseits kristalliersiert sich da bei mir eine Erkenntnis heraus...
Unicode ist die Basis für Python, Ausgabe, für jedermann in seinem Zeichensatz lesbar, muss !richtig! kodiert werden.
Das wandeln in Python-Standard-Unicode: decode(charset) (-> das Charset der Quelle. Also von was er nach UNICODE wandeln soll). Das wandeln in 'lesbare' Zeichen .encode(charset) (-> das Charset in welches gewandelt werden muss um es dann bei MIR richtig darzustellen).

Also so in etwa:
"Quell-Charset: utf-8" -> DECODE("utf8") -> UNICODE -> "Ziel-Charset: ENCODE("iso 8859-1")

Sagt jetzt bitte ich irrem ich nicht ganz gewaltig sondern nur ein wenig..
rmp

Jein.

Basis für so ziemlich jede Programmiersprache sind sinnfreie Bytefolgen. Die waren (damals, als die Welt noch einfacher und im wesentlichen ASCII basiert war *g) praktisch gleichbedeutend mit (sinnbehafteten) Zeichen. Anmerkung: Wir Menschen sagen "Zeichen" oder "Buchstabe", eben weil das für uns Sinngehalt hat. In einer CPU schwirren nur Bytes herum.

Da aber UTF-8 so viel Bedeutung gewonnen hat und Menschen, wie du nun hautnah erlebt hast, gerne mit menschenverständlichen Zeichen hantieren, hat Python die "u'...'" Notation eingeführt. Eigentlich ist das schnurz, aber z.B. beim Sortieren ist es wichtig (und relativ prozessorteuer, weil UTF-8 ein in mancher Hinsicht hässliches Konstrukt ist, weil es (anders als z.B. 4-Byte-Sequenzen) keine Zeichen mit fixer Länge hat (dafür aber für den größten Teil der Welt speicherplatzschonend ist)).

Also:

Von draussen reinlesen -> decode
Nach draussen schreiben -> encode
*in* Python (mit UTF codiertem Quellcode) -> u'München'
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Aaaaaahhhh.
Ist schon lange her und darum habe ich von dem viel gelesenen (Online-Tutorials) von vor 5 Monaten nicht alles 'behalten' können!

Ich habe jetzt München angezeigt bekommen und zwar ohne encodieren zu müssen!
Ich hätte ja bereits nach dem zweiten Post von BlackJack darauf kommen müssen!
Ich habe den Content einer Variable zugewiesen und bin davon ausgegangen das es ein String ist. Es ist aber kein String sondern nach wie vor eine Liste.
Und Listen kann man nicht als ganzes kodiert ausgeben sondern muss das Listelement 'ansprechen'. Also in meinem Fall:

Code: Alles auswählen

print(stadt(0))
Und siehe da: es klappt.
Es ist mein Fehler weil BlackJack das ja bereits gesagth abe, ich es lediglich nicht verstanden habe (es aber dachte verstanden zu haben) und es dann 'übergangen'....

Ich poste das hier für alle anderen Beginner die eventuell den gleichen Fehler begehen. Lernt aus meinen Fehlern ;)

Dennoch:
bitte nochmals abnicken oder verneinen wenn ich das mit den decoden()->UNICODE->encoden()->lesbar richtig/falsch verstanden habe.

Vielen vielen Dank an euch alle! So viel Geduld ist heutzutage in anderen Foren nicht selbstverständlich!
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@kaineanung: "decoden()->UNICODE->encoden()->lesbar" ist falsch. Je nach Ein- oder Ausgabeweg muß man unterschiedlich vorgehen. Fertige Pakete (HTML-Parser, Datenbank, , Excel-Datei, GUI-Rahmenwerke, Konsole?, ...) unterstützen schon von Haus aus Unicode, übernehmen also das En-/Decodieren für Dich. Bei allem was Bytes verarbeitet (Dateien, Netzwerk), bist Du für die Kodierung verantwortlich, das was dann rauskommt, ist aber auch nicht mehr lesbar, sondern muß wieder von einem anderen Programm in eine Form gebracht werden, damit es lesbar wird.

@rmp: die Bytefolgen haben sehr wohl einen Sinn, für Computer und für Leute, die Computer programmieren. Die Python-u-Notation hat nichts mit UTF-8 zu tun. Und ob etwas schwierig zu sortieren ist, braucht Dich als Python-Programmierer nicht zu interessieren. Das u bedeutet, dass es sich nicht um Bytes sondern um Unicode-Codepoints handelt. Wie diese Codepoints intern repräsentiert werden, braucht Dich nicht zu interessieren und ist auch zwischen unterschiedlichen Python-Implementierungen verschieden.
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

kaineanung hat geschrieben:bitte nochmals abnicken oder verneinen wenn ich das mit den decoden()->UNICODE->encoden()->lesbar richtig/falsch verstanden habe.
Ich denke, deine Frage ist falsch gestellt. Denn aus Sicht eines menschlichen Anwenders handelt es sich bei dem, was er sieht, weder um Bytes, noch um Unicode-Codepoints, sondern schlichtweg um bekannte Schriftzeichen aus einer bestimmten Sprache (zumindest, wenn man alles richtig gemacht hat) - außer er fordert explizit die "tatsächliche Darstellung" an (d.h. in Python z.B. via `repr()`). Das ist also nochmal etwas völlig anderes im Gegensatz zu den beiden auf technischer Ebene angesiedelten Konzepten von Bytes und Unicode.

Das heißt: Die textuelle Ausgabe auf dem Bildschirm wird nicht lesbarer für dich, nur weil du Unicode oder Bytes als Grundlage genommen hast. Es geht schlichtweg darum, dass das Terminal etwas erhalten hat, mit dem es sinnvoll (d.h. erwartungskonform) umgehen kann. Der Knackpunkt ist die Verwendung der richtigen (d.h. dem ausgebenden Medium angepassten) Kodierung. Dann wird alles gut. ;)
rmp

Sirius3 hat geschrieben: @rmp: die Bytefolgen haben sehr wohl einen Sinn, für Computer und für Leute, die Computer programmieren. Die Python-u-Notation hat nichts mit UTF-8 zu tun. Und ob etwas schwierig zu sortieren ist, braucht Dich als Python-Programmierer nicht zu interessieren. Das u bedeutet, dass es sich nicht um Bytes sondern um Unicode-Codepoints handelt. Wie diese Codepoints intern repräsentiert werden, braucht Dich nicht zu interessieren und ist auch zwischen unterschiedlichen Python-Implementierungen verschieden.
Schon mal einen Interpreter gebaut? Anscheinend nicht. Aus genau den von mir geschilderten Gründen kannst du in C casten und aus einem Buchstaben eine Zahl machen oder auch einen Pointer. Weil eine Byte ein Byte ist für eine CPU und sonst gar nichts. Punkt.

Und doch, das interessiert mich als Python Programmierer sehr wohl, ob das schwierig zu sortieren ist. Weil es nämlich bei einem Programm den Unterschied zwischen brauchbar schnell und unbrauchbar langsam ausmachen kann. Sicher, fürn Hobbyprogrämmelchen für Mutti isses egal, aber wenn du ein Programm bei einem Kunden ablieferst, dann wäre es schon sehr wünschenswert, wenn es leidlich performt. Und dazu muss ich als Programmierer eine Ahnung haben, wie die von mir genutzten Konstrukte intern funktionieren.

Was meinst du wohl, warum es UTF-16 gibt? Weil man in 16 Bits im wesentlichen alle aktuell gebräuchlichen Zeichen der Welt unterbringen kann und dazu nur 2 Bytes braucht. Bei einer großen Datenbank z.B. macht das den (teuren) Unterschied zwischen 512 GB (UTF-16) RAM und 1 TB (UTF-32) RAM. Sie auch UTF Baseplane.
Hat nur ein hässliches Problem: Die ostasiatische plane liegt wo ganz anders und ausserdem maulen dann asiatische Gelehrte, Ägyptologen, Musiker, etc. Und siehe an: Gar nicht wenige Programme (insb. für den asiatischen Markt) arbeiten intern mit UTF-32. Vorteil: Alle Zeichen sind gleichgroß, man kann wieder mit Indizes adressieren, sehr einfach (und schnell) vergleichen, usw. Nachteil: Übel Speicherverbrauch. Deshalb haben manche (sogar Betriebssysteme) zumindest eine ganze Weile lang mit UTF-16 gewurstelt.

UTF-8 ist für uns Europäer und den amerikanischen Kontinent ein attraktiver Kompromiss, weil wir meist kaum mehr Speicher brauchen als mit ASCII, aber die Option haben, auch z.B. chinesische Zeichen verarbeiten zu können. Das ist der Vorteil. Die Nachteile sind: Im schlechten Fall erheblich mehr Speicherverbrauch als UTF-32 und vor allem grundlegende algorithmische Veränderungen. Indizes kann man vergessen (in den Innereien. Nach aussen hin kann man das simulieren). Und es ist hässlich und aufwendig beim Interpreter/Compilerbau. Weil du nämlich deinen Stringobjekten 2 Längenvariablen mitgeben musst; eins für die Speichergröße und eins für die logische Stringlänge. Beispiel: "München" ist 7 Zeichen aber 8 Bytes lang. Und jedes Mal wenn im Code steht "x = mein_string[3]" musst du intern Zeichen für Zeichen entlangwandern und die höchsten Bits auswerten um zu sehen, wieviele Bytes das Zeichen braucht. Oder aber - und das machen durchaus einige - Du arbeitest intern einfach mit 2 oder 4 Byte Zeichen.

Für die Erwähnung von Unicode Codepoints gebe ich dir n Extrapunkt, weil das durchaus relevant (wenn auch leider für viele schwer zu verstehen) ist zum Thema hier. Zur Frage, ob mich das interessieren muss allerdings, widerspreche ich dir nochmal; siehe die Ausführungen eben.
BlackJack

@rmp: Alle Zeichen sind nie gleich gross bei Unicode denn Codepoint != Zeichen.

Die Interna der Unicode-Repräsentation hat Dich bei Python (und auch anderen Programmiersprachen) insofern nicht zu interessieren als das sie ein Implementierungsdetail ist das sich nicht nur bei verschiedenen Implementierungen unterscheiden kann, sondern sogar ein und die selbe Implementierung kann Unicode-Objekte intern unterschiedlich je nach Objekt speichern. Und das ist nicht nur Theorie, aktuelle CPython-Implementierungen praktizieren das auch, so dass reiner ASCII-Text nicht mit 32 Bit pro Codepoint zu Buche schlägt, aber trotzdem auch alles ausserhalb des Baseplane kodiert werden kann.

Vor allem braucht es Dich nur einmal interessieren: Wenn Du Dich für oder gegen Python entscheidest. Denn Du kannst da letztendlich ja nichts dran ändern, ausser Du willst am Interpreter selbst etwas ändern für Dein Programm oder einen eigenen Interpreter schreiben. Das wäre eher unüblich. Bevor man sich das ans Bein bindet würde man sich eine andere Sprache suchen wenn man mit der Implementierung nicht zufrieden ist.

„München“ mag zwar sieben Zeichen haben, das kann man aber in 7 oder aber auch 8 Codepoints kodieren. Für's Dateisystem auf MacOS werden beispielsweise 8 Codepoints verwenden. Die UTF8-Kodierung *davon* hat dann 9 Bytes. Je nach dem ob man mit Zeichen nun tatsächlich Glyphen meint oder nur Codepoints muss man, egal ob die Codepoints nun in UTF-8, -16, oder -32 im Speicher gehalten werden, immer von vorne durchgehen und zählen wenn man das x-te Zeichen ermitteln möchte. Ein Grund warum es bei Programmiersprachen/Bibliotheken auch eine Bewegung hin zu zu UTF8 als interne kodierung gibt. Go und Rust wären da Beispiele.

Python sortiert Unicode-Objekte ganz simpel nach Codepoints. Das kann also durchaus *nicht* das sein was man haben möchte und ”teure” Zusatzoperationen nötig machen, eventuell sogar solche die sich dann tatsächlich aus Gründen der Geschwindigkeit in reinem Python verbieten weil's einfach zu langsam würde. Letztlich ist das sortieren von echten Zeichenketten aber sowieso ein sehr komplexes Thema das man nicht universell für alle zufriedenstellend abhandeln kann, wo man also letzten Endes eine spezielle Bibliothek für benötigt wenn man andere Anforderungen hat als simples vergleichen von Codepoint-Werten.
rmp

BlackJack

Wir können das noch ewig hin und herdrehen und immer feinere Unterscheidungen treffen, nur: Wenn ich mit Texten arbeite - und das ist nunmal ein klassischer Aufgaben Schwerpunkt bei Scriptsprachen - dann wird es mir weder egal sein, ob im Schnitt 1 Byte oder 4 Bytes pro Buchstaben verwendet werden und es wird mir auch nicht egal sein, ob Sachen wie "len()" oder indizes simple, schnelle Operationen sind, oder ob dahinter aufwendige Algorithmen stecken, egal wie das an der Oberfläche aussieht.

Ich denke, du weisst so gut wie ich, dass (wohl nicht zufällig) relativ häufig Leute Probleme in diesem Bereich haben (encode/decode, siehe diesen thread). Es macht also durchaus Sinn, mal etwas hinter die Kulissen zu schauen, auch wenn, da hast du natürlich Recht, der Durchschnittsuser wenig ändern kann und damit leben muss, wie es nunmal implementiert ist in Python.

Und ehe nun wieder Belehrungen und Zweifel kommen (womit ich nicht explizit dich meine), hier gleich der Disclaimer: Ich mag Python. Sehr. Sonst würde ich es ja nicht seit Ewigkeiten als mit Abstand bevorzugte Script Sprache verwenden.
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@rmp: Soweit ich weiß, ist eine Unicode-Implementierung nicht gezwungen, für Konvertierungen mit einer Kodierung, die normalerweise N Bytes pro Zeichen belegt, intern auch tatsächlich N Bytes zu verwenden. Wichtig ist nur, dass die nach außen gezeigte Darstellung der Zeichen auch tatsächlich zur gewählten Kodierung kompatibel ist. Insofern relativiert dies deine Anmerkungen zum Speicherverbrauch von Unicode-Objekten, weil da halt durchaus weniger Speicher verbraucht werden kann als du denkst.

Auch auf Erkenntnisse zur Performance einzelner Kodierer würde ich mich mittelfristig nicht verlassen. CPython hat in der letzten Zeit immer wieder mal diverse Performance-Optimierungen an verschiedenen De- bzw Encodern erhalten. Was heute aus Geschwindigkeitsgründen (sofern diese denn relevant werden) als sinnvoll erscheint, kann in 2-3 Jahren schon wieder anders aussehen. Zumindest für Code, der auch unter anderen Interpreter-Versionen laufen soll, kann man also gar nicht so sicher sein, dass man tatsächlich die für die jeweilige Umgebung schnellste Variante ausgewählt hat.

Was ich sagen will: Man muss diese beiden Punkte nicht völlig ignorieren, aber man sollte sich auch nicht zu sehr bei seiner Entscheidung darauf verlassen. Und oftmals bestimmen ja noch ganz andere Faktoren, welche Kodierung gewählt wird, sodass rein Python-bezogene Aspekte dadurch in den Hintergrund rücken.
BlackJack

@rmp: Die Probleme die die Leute mit (de)kodieren haben, haben nichts mit der internen Darstellung oder der Implementierung von Unicode-Objekten zu tun sondern durch die Bank mit einem fehlenden Verständnis vom Unterschied zwischen Bytewerten, Zeichen, und Zeichendarstellungen. Für die meisten ist das halt das gleiche oder sie haben darüber noch nie nachgedacht, fallen dann darüber das es nicht *den* Bytewert für ein 'ä' gibt, und sind frustriert das das nicht automatisch zuverlässig ermittelt werden kann, sondern das man sich damit tatsächlich ernsthaft auseinandersetzen muss.

Was das Ermitteln der Länge (wie auch immer man das definiert) angeht, erwarte ich einfach dass das eine Operation mit konstanter Laufzeit ist. Wie die Entwickler das implementieren ist mir egal. Ob sie das so tun im Grunde auch, solange ich das nicht merke. Was den Speicherverbrauch angeht: Ist bei heutigen Speichergrössen auch nicht mehr so wirklich wichtig. Andererseits kodiert CPython intern ja schon in Abhängigkeit vom Inhalt der Zeichenkette platzsparender als UCS4.
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@rmp: Du versuchst mich persönlich zu beleidigen, viel Spaß dabei. Bei Skriptsprachen stehen primär weder die Ausführungsgeschwindigkeit, noch der Speicherverbrauch im Vordergrund. Wer gerne double in Pointer castet, kann das in C gerne machen. In höheren Sprachen wird absichtlich ein String von reinen Bytes abstrahiert. Damit hat man die Freiheit, die optimale Repräsentation zu wählen. UTF-16 gibt es, weil als man die Designentscheidung für 16bit-Zeichen getroffen hat (sowohl Java als auch Windows-NT stammen aus den frühen `90ern) gab es nur UCS-2.
Ich schreibe auch für "Kunden", und mir ist so ziemlich egal, wie das meiste Intern funktioniert. Meistens achte ich nicht darauf, dass Python die Cachelines des Prozessors optimal füllt. Für meine 192-Bit Arithmetik habe ich dagegen alle Register richtig hinsortiert um noch einige Takte zu sparen. Unterstell mir also nicht, ich hätte keine Ahnung. In 99% der Fälle sind Deine Optimierungsphantasien vergeudete Zeit, die Du besser in sinnvolles steckst.
Antworten