Thread JSON und utf8
(25 answers)
Opened by rosti at 2018-05-06 18:58
Da muss ich doch nochmal nachhaken. Ich habe schon oft gehört / gelesen, dass irgendeine Software beim Encoding Mist mache, und in den meisten Fällen liegt der eigentliche Fehler aber bei irrtümlichen Annahmen, zu denen die Encoding-Welt reichlich Anlass bietet. Das wird jetzt etwas länglich.
Die erste oft getroffene Annahme ist, dass use utf8 etwas damit zu tun habe, wie Perl seine I/O macht. Das ist aber falsch: Mit dieser Angabe sagt man dem Perl-Parser, dass diese Quelldatei in UTF-8 codiert daherkommt (https://perldoc.perl.org/utf8.html). Wer seine I/O per Default anders codiert haben will, kann das mit use open ':encoding(UTF-8)' machen - aber wieder falsch wäre die Annahme, das wirke auch auf STDOUT: Das wirkt nur auf Dateien, die man selber unter dem gleichen Scope aufmacht. Quote- nein, das ist nicht blödsinning, sondern entweder falsch oder richtig, je nachdem, wie die Datei abgespeichert wurde. In dem Quelltext steht z.B. das Literal "Ihre Lösung war richtig!". Welche Byte-Sequenz für das 'ö' auf die Platte kommt, hängt nun davon ab, in welchem Encoding der Editor das wegschreibt. Und weil inzwischen 2018 ist, nehmen wir mal an, es sei UTF-8. Der Perl-Parser trifft aber keine Annahmen: Wenn nix drinsteht, parst er den Text in seinem Default-Encoding, und das ist so in etwa beinuhe ISO-LATIN-1. Das "ö" in einer UTF-8-Quelldatei sind zwei Bytes, also in diesem Encoding zwei Characters. Zum Ausprobieren: Speichert man diesen Quellcode als UTF-8, dann ist die Länge 2, speichert man ihn als ISO-LATIN-1, dann ist die Länge 1. Wer will, kann's auch als Einzeiler in sein Terminal klopfen. Mal unter Linux, mal unter Windows cmd.exe (mit " anstelle der ' bei cmd.exe), gerne auch PowerShell oder Cygwin Bash: Code: (dl
)
perl -e '$l=q(ö); print length $l,qq(\n)' Unter heutigem Linux ist das 2, weil die heutigen Shells/Terminals UTF-8 als Encoding eingestellt haben. Unter Windows cmd.exe eine 1. Im Gegenbeispiel speichere man folgende Datei unter ISO-LATIN-1 ab und verfüttere sie an Perl: Da kommt dann von Perl zu Recht die Beschwerde Malformed UTF-8 character (1 byte, need 4, after start byte 0xf6). QuoteWas nach STDOUT geht, wird von Perls I/O-Layer so aus interner Darstellung in Bytes umgewandelt, wie man es ihm angeschafft hat. Und wenn man nichts explizit anweist, dann wirkt der Default-I/O-Layer, und der ist nicht UTF-8. Wenn man ein UTF-8-codiertes "ö" im Default-Encoding einliest und hinterher im Default-Encoding wieder ausgibt, dann liefert das ein UTF-8-codiertes "ö", weil das Default-Encoding Bytes liest und Bytes rausschreibt. Für Perl intern sind das zwei Bytes - und auch zwei Characters. Deswegen gehen solche Konstruktionen erst dann für alle sichtbar in die Hosen, wenn gleichzeitig "richtige" UTF-8-Characters verarbeitet werden sollen. QuoteAbsolut richtig. Das fängt aber schon bei der Quelldatei an, siehe oben. Und deswegen macht hier auch JSON.pm keinen Mist, sondern tut genau das, was es soll. Mit utf8(0) läßt es die Strings, wie sie sind, und man muss es selbst im UTF-8-Encoding rausschreiben. Mit utf8(1) wandelt es einen String in UTF-8-codierte Bytes um, und man muss es im Byte-Encoding rausschreiben. Wenn der String aber schon vorher vermurkst war, weil ein UTF-8-codiertes "ö" im Default-Encoding eingelesen wurde, dann kann JSON das nicht reparieren. |