Probleme mit Umlauten und Sonderzeichen

Ein Problem, auf das zahlreiche Entwickler immer wieder stoßen, sind Umlaute. Wer kennt sie nicht – Zeichen wie ö, ä oder ü? Viele von uns haben schon einmal PHP-Code gesehen, in dem Entwickler mittels utf8_encode() oder utf8_decode() versucht haben, irgendwie den Zeichensatz zurechtzubiegen, ohne genau zu wissen was sie da eigentlich machen, oder weshalb die seltsam anmutenden Zeichen entstanden sind. Oft treten die Probleme auch nach einem Umzug der Datenbank auf. Beispielsweise wenn ein Content Management System auf einen neuen Server portiert werden soll. Erläutern wir also kurz wo die Probleme mit Umlauten/Sonderzeichen liegen und wie man sie vermeiden kann.

Erst einmal sollte man sich verinnerlichen, welche Teile der Anwendung vom Encoding betroffen sind:

  • Dateisystem (Dateinamen & Pfade, nicht die Datei selbst)
  • Dateien (PHP-Dateien genauso wie Datendateien)
  • Datenbank
    • Die Verbindung zur Datenbank
    • Standard-Encoding für die Datenbank
    • Standard-Encoding für die Tabelle
    • Encoding für die Zelle
  • Webserver
  • Encoding der Ausgabe des PHP-Interpreters
  • Standard-Encoding des Browsers
  • Encoding im HTTP-Header (z.B. „charset=utf-8“)
  • Angegebenes Encoding in der entstandenen HTML-Ausgabe (per meta-Tag)
  • Benutzer-Eingaben (z.B. aus Forms)

Diese Angaben beruhen natürlich auf der Annahme, dass man eine HTML-Datei erzeugen und diese per Webserver in einem Browser wieder ausgeben möchte. Versendet man beispielsweise E-Mails per PHP-Skript, so kann man in obiger Liste den Browser durch das Mail-Programm ersetzen und die HTML-Ausgabe fällt – zumindest bei Text-Mails – weg.

Ist das Encoding kaputt, muss man im schlimmsten Fall alle möglicherweise betroffenen Stellen prüfen. Bricht das Encoding in einem der vielen Schritte, ist es danach i.d.R. überall kaputt. I.d.R. deshalb, weil man natürlich mit Funktionen wie utf_encode() ggfs. die betroffenen Zeichen wieder geraderücken kann. Das ist aber nur die halbe Wahrheit. Sobald man sich nämlich aus dem westeuropäischen Zeichensatz herausbewegt, sind die Umlaute dann auch wieder kaputt. Das liegt an der intern fest kodierten Verwendung von ISO-8859-1, dem westeuropäischen Zeichensatz, innerhalb von utf8_encode/utf_decode.

In den letzten Jahren hat sich UTF-8 als neuer Standard im Web festgesetzt. Noch vor einiger Zeit bestimmten die ISO Codes 8859-xy maßgeblich die Zeichenkodierungen. Leider haben diese aber den Nachteil, dass sie immer nur einen ziemlich beschränkten Teil aller zurzeit verfügbaren Zeichen darstellen können und man deshalb in mehrsprachigen Systemen viele unterschiedliche Zeichensätze einsetzen musste. Mit der Einführung von UTF-8 benötigt man nur noch eine Kodierung.

Wie kann man per PHP auf das Encoding Einfluss nehmen?

Letztendlich kann ich nicht auf alle Webserver-Konfigurationen & Co. eingehen. Dahingehend konzentriere ich meine nachfolgenden Beispiele hauptsächlich auf PHP und MySQL.

Bereits in der php.ini gibt es die per default deaktivierte Option:

default_charset = "UTF-8"

Diese Option bewirkt in eingeschaltetem Zustand eine Erweiterung des Header-Eintrages „Content-Type: text/html“ um „;charset=UTF-8“. Für Apache-Nutzer: Gleiches kann man erreichen, indem man in der Datei „/etc/apache2/conf.d/charset“ die nachfolgende Zeile durch das Entfernen von „‚#“ aktiviert:

#AddDefaultCharset UTF-8

In beiden Fällen ist ein erneutes Einlesen der Konfigurationen (z.B. durch einen Neustart des Webservers) erforderlich. Sind übrigens beide Optionen aktiviert – also die in der php.ini und die in der Apache-Config, so gewinnt die von PHP.

Wer nach dem Aktivieren von einer der obigen Optionen noch immer Probleme mit dem Zeichensatz hat und der Text aus einer einfachen Datei kommt (dabei ist es irrelevant, ob der Text innerhalb der PHP-Datei oder einer separaten Datei steht) prüfen,

…ob der Header nun auch richtig gesendet wird. In Firefox erhält man diese Information beispielsweise über die rechte Maustaste und den Menüpunkt „Seiteninformationen anzeigen“ im Reiter „Header“. Dort kann der Punkt „Content-Type: text/html; charset=utf-8“ geprüft werden.

…ob die Datei auch im richtigen Encoding geschrieben wurde. Dabei ist es irrelevant, ob der auszugebende String innerhalb der PHP-Datei selbst, oder einer separaten Datei steht (von Datenbanken mal abgesehen – darauf gehe ich später ein).  In VIM kann UTF-8 beispielsweise mit folgendem Eintrag in der .vimrc erzwungen werden:

set fileencoding=utf-8

In praktisch allen aktuellen IDE’s und Texteditoren gibt es eine entsprechende Option, das Fileencoding einzustellen. Eine Ausnahme stellt hier wohl Netbeans dar, das es nur erlaubt, das Encoding für ein gesamtes Projekt zu bestimmen :-(. Wichtig zu wissen ist wahrscheinlich auch noch: Nur weil man durch Aktivieren einer der weiter oben beschriebenen Optionen im Header dem Content-Type ein charset-Attribut hinzufügt, bedeutet das nicht das automatische Konvertieren der Ausgabe und in unserem Fall somit der Datei. Die Datei muss bereits im richtigen Format vorliegen.

Bei der Verbindung zu MySQL sieht es anders aus. Hier wird bei der Angabe des Zeichensatzes auch automatisch konvertiert. Der entsprechende SQL-Befehl dafür lautet:

SET NAMES utf8

Natürlich muss man aber auch hier erst einmal sicherstellen, dass die Daten in der Datenbank im richtigen Format gespeichert wurden (genau wie bei Dateien auch) und dass zusätzlich auch das Feld das korrekte Encoding erhalten hat. Beispiel:

In einer Zelle steht ein gemischt-kodierter latin1- und utf8-String „üöäüöä“. Der erste Teil „üöä“ steht mit latin1 kodiert, der zweite „üöä“ mit utf-8 in der Datenbank innerhalb einer Zelle. Nun tätigen wir folgende Abfrage:

SET NAMES latin1; SELECT myfield FROM mytable;

Ergebnis: üöä��

Nun das Ganze in utf-8:

SET NAMES utf8; SELECT myfield FROM mytable;

Ergebnis: üöäüöä

Wenn also bereits der Inhalt einer Zelle nicht korrekt geschrieben wurde, kann auch kein „SET NAMES“ mehr helfen.

Fazit: Zeichensätze sollten über mehrere Systeme hinweg nicht gemischt werden. Angefangen beim Einlesen der Eingabedaten bis hin zur Ausgabe im Browser sollte der gleiche Zeichensatz beibehalten werden. Dann sind auch Funktionen wie utf8_encode() und utf8_decode vollkommen überflüssig. Wer diese benötigt, sollte sich fragen, ob es nicht sinnvoller ist die Quell-/bzw. Zieldaten gleich in UTF-8 zu halten. Wer kein UTF-8 benutzt und gleichzeitig mehrere Sprachen mit ihren verschiedenen Sonderzeichen unterstützten muss, sollte außerdem vorsichtig Kodierungen umgehen. UTF-8 nimmt einem hier eine Menge Arbeit ab.

7 Gedanken zu „Probleme mit Umlauten und Sonderzeichen

  1. Ich würde zu deiner Auflistung oben noch den Punkt „Eingaben vom User“ hinzufügen, da ich öfter das Problem hatte, dass die User Eingabe als latin1 gesendet werden. Die Lösung ist natürlich wieder einfach, im muss man als einziges charset UTF8 erlauben.

  2. Ein wirklich guter Artikel über die Encoding-Probleme mit denen wir kämpfen müssen!

    3 Dinge würde ich ansprechen:
    – Bzgl. AddDefaultCharset meinst du wahrscheinlich „einkommentieren“ und nicht „auskommentieren“
    – Der Rat, direkt die Quell-/bzw. Zieldaten in UTF-8 zu halten ist gut und richtig. Wenn man jedoch mit anderen Systemen kommuniziert kommt man um eine Umwandlung nicht herum wenn das andere System kein UTF-8 liefert. Solange es konstant und voraussagbar ist kann man das leicht beheben.
    – Falls doch der Fall eingetreten ist und die Datenbank in Nicht-UTF-8 vorliegt gibt es kleine Tricks um sie zu konvertieren, zum Beispiel exportieren als SQL-Text, dann mit einem Editor der Wahl umwandeln, die Spalten-Kollationen etc. umstellen und wieder importieren.

    Artikel ist gebookmarkt, kann man prima verwenden zur Aufklärung unwissender Kunden, denen man x Stunden Aufwand wegen Encoding-Problemen bei Legacy-Systemen erklären muss.

  3. Beim SQL-Export und -Import kann man durchaus etwas tricksen:
    –default-character-set=utf8 (oder was auch immer für Kodierung)
    Den Umweg über den Editor kann man sich somit sparen. …Außer, man hat es so versemmelt, dass keiner mehr so richtig weiß, welcher Byte zu welchem Zeichensatz gehört 😉

    Aber ansonsten ein guter zur Verständigung der Zeichensatz-Welten.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *