MySQL vs. UTF-8 (vs. CodeIgniter)

MySQL kann UTF-8… falsch. Besser gesagt: Ungenau. MySQL kann UTF-8, aber das heißt da anders:

Ein Zeichen in UTF-8 ist (bis zu) vier Bytes lang, “UTF-8” in MySQL kann aber nur bis zu drei. Deswegen fällt der Unterschied oft gar nicht auf, oder erst beim ersten Zeichen, das vier Bytes braucht. Will man echtes UTF-8, muss man einiges umkonfigurieren; in Kurzform:

  1. SQL muss in der Version 5.3.3+ vorhanden sein, siehe Link oben
  2. Jede Datenbank, jede Tabelle (ggf. jede Spalte) müssen auf “CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci” umgestellt werden
    • Dabei zu beachten: InnoDB kann wohl nur 767 Bytes pro Index Schlüssel. Wenn man vier (statt drei) Bytes pro Zeichen rechnet, sind das bsplw. nur 191 (statt 255) Zeichen!
    • Das gilt auch für Typen wie TINYTEXT, die wohl nur 255 Bytes = 63 Zeichen bei 4 Byte/Zeichen speichern können. Ggf. ist hier ein anderer Typ zu wählen
  3. ggf. Server, Client, etc. umstellen (dabei hilft “SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';“)
  4. ggf. Datenbank reparieren und optimieren (war bei mir nicht nötig, ich habe alle Tabellen neu angelegt):

bzw.

Insbesondere bei Punkt 3 hängt es bei CodeIgniter, btw. Wenn man in der Datenbank-Config

 stehen hat, dann ist das ebenfalls nicht das echte UTF-8. Stattdessen korrekt ist:

Update

Das Problem betrifft auch mysqldump: Will man die eine utf8mb4-Datenbank dumpen, muss man das explizit sagen, denn mysqldump nutzt per Default nur utf8 (Quelle):

Das ist so tief verankert, dass auch Dumps über Tools wie Sequel Pro nur utf8 nutzen m(

Java/Velocity, XML/JSON: UTF-8 [UPDATE]

Wer Velocity auf ‘nem Tomcat laufen hat, stolpert vielleicht mal über das Problem, dass das Character Encoding falsch gesetzt ist. Sprich, dass Umlaute falsch gerendert werden.

Nun könnte man vermuten, dass das am String selbst liegt. Tut es nicht: In Java sind Strings UTF-16.

Dann könnte man den Fehler in Velocity suchen. Etwa könnte man die Templates explizit als UTF-8 öffnen:

Das hat bei mir nicht geholfen. Allerdings kann man Velocity das Default-, Input- und Output-Encoding vorgeben:

Das scheint schon mal was zu bewirken. Allerdings wird einem hier auffallen, dass die meisten (wenn nicht alle) Umlaute nicht im Template liegen, sondern dort nur reingeschrieben werden. In unserem Fall wurden zum Beispiel zum Teil Daten eines Webservice, zum Teil aber auch lokal gespeicherte XMLs verwendet. Die XMLs müssen UTF-8-kodiert gespeichert worden sein:

Und sie müssen vor allem auch UTF-8-kodiert gelesen werden:

Update

Kommen die XML-Daten (hier als ZIP) aus der Schnittstelle, müssen sie als UTF-8 in einen String konvertiert werden:

So. Last but not least müssen die Daten, die serverintern jetzt in UTF-8 vorliegen sollten, auch als solches an den Browser ausgegeben werden. Während normalerweise der Writer des Response ausreicht, um den OutputStream dieses Response zu handlen:

, muss man diesen für UTF-8 manchmal mit einem Writer mit explizit gesetztem UTF-8 ersetzen (Danke an Ralf!):

Achtung: Für JSON-Response musste ich dagegen das Encoding des Response selbst setzen:

Vermutlich ist das ein wenig kompliziert gemacht (ich meine, hey, das muss einfacher gehen!), aber so scheint es hier zu funktionieren. HTH

Update

Für Spring gilt es außerdem, dieses Setting zu beachten: