toscho.design

Zeichenkodierung angeben


Wir haben das Prinzip der Zeichenkodierung verstanden und können miteinander darüber reden. Jetzt müssen wir nur noch die Software unserer Empfänger informieren.

HTTP

Per Transfer-Protokoll geben wir die Zeichenkodierung immer an. Es kostet keine Mühe und spart viel Ärger.

Im Webserver Apache schreiben wir in die Datei .htaccess:

AddDefaultCharset UTF-8

Jetzt fügt der Apache jedem Content-Type-Header den Parameter charset=UTF-8 hinzu. Damit erwischen wir nicht nur XML, sondern auch HTML, CSS und alle anderen Text-Dateien.

Etwas spezieller, dafür gleich mit richtigem MIME-Typen sieht das so aus:

AddType text/css;charset=UTF-8 .css
AddType text/html;charset=UTF-8 .html
AddType application/xhtml+xml;charset=UTF-8 .xhtml

In PHP-Skripten schreiben wir hier:

header('Content-Type: text/html;charset=utf-8');

Das Ergebnis können wir auf web-sniffer.net überprüfen: Im Antwort-Header steht dann so etwas:

Content-Type: text/html;charset=UTF-8

Die Angabe der Zeichenkodierung ist der schwache Punkt bei der sonst so praktischen Trennung von Transport und Ressource: Wird sie falsch oder gar nicht übermittelt, kann das Dokument nicht richtig verarbeitet werden. Deshalb gönnen wir uns hier ein wenig Redundanz und geben sie noch einmal im Dokument selber an. Die meisten künstlichen Sprachen haben dafür spezielle Mechanismen.
Vorsicht: Wenn sich die Angabe des HTTP-Headers mit der des Dokumentes widerspricht, gewinnt der HTTP-Header. Wir müssen hier sehr auf Konsistenz achten. Und natürlich sollte diese zusätzliche Angabe vor dem ersten Nicht-ASCII-Zeichen stehen.

XML

Jede Software, die XML interpretieren kann, verarbeitet auch UTF-8 und UTF-16. Wenn wir ein XML-Dokument wie etwa einen Newsfeed, SVG oder XHTML mit dem passenden MIME-Typen verschicken, brauchen wir im Prinzip überhaupt nichts zu tun. Wir können sogar die XML-Deklaration weglassen. In der steht sonst die Zeichenkodierung:

<?xml version="1.0" encoding="UTF-8"?>

Wer sicher gehen will, der schreibt sie dennoch hin. Es schadet nicht. Nur wenn jemand ein Dokument in XHTML verfaßt, aber als HTML ausgibt (mit dem MIME-Typen text/html), dann geht der Internet Explorer 6 in den Quirksmodus und interpretiert das Stylesheet anders.

HTML

In HTML-Dokumenten können wir zusätzlich zum HTTP-Header die Kodierung im Dokument angeben:

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

Günstig ist diese Angabe, wenn wir das Dokument nicht per HTTP versenden, sondern es beispielsweise auf der Festplatte abspeichern und lokal aufrufen. Opera und selbst der Internet Explorer schreiben sie beim Abspeichern meistens ganz allein in den Code; andere Browser sind noch nicht soweit, also helfen wir ihnen.

HTML 5

In HTML 5 wird alles einfacher: Da geben wir im Meta-Element nur noch die Zeichenkodierung an und nicht mehr den MIME-Typen, der hier ohnehin wirklungslos bleibt.

<meta charset="utf-8">

Das verstehen alle aktuellen Browser.

CSS

Auch in CSS-Dateien können wir nachhelfen, indem wir an den Anfang schreiben:

@charset "UTF-8";

Die Unterstützung in den aktuellen Browsern ist aber eher unzuverlässig.

Skripte und Textdateien

In Programmiersprachen und reinen Textdateien existiert oft kein spezieller Ausdruck für die Angabe der Zeichenkodierung. Dann brauchen wir entweder ein BOM oder ein Cookie.
Das BOM ist komplizierter, also fangen wir damit an.

BOM

UTF-16 und UTF-32 treten in zwei Notationen auf: als Big-Endian (BE) und als Little-Endian (LE). Bei Big-Endian stehen die »signifikanten« Bytes vorne, bei Little Endian hinten.
Um nun dem Empfänger einer Datei mitzuteilen, welche Notation man benutzt, kann man an die erste Stelle ein Byte Order Mark stellen, oder kurz: ein BOM.

Dieses BOM kodiert den Code point U+FEFF, der früher die Bedeutung ZERO WIDTH NON-BREAKING SPACE (ZWNBSP) oder »nicht umbrechendes Leerzeichen ohne Breite« trug. Dafür sollte heutzutage das Zeichen U+2060 WORD JOINER benutzt werden.

Da UTF-8 nur eine Byte-Reihenfolge hat, braucht es kein BOM, um eine anzugeben. Dennoch kann eine in UTF-8 kodierte Datei mit einem U+FEFF beginnen. Dann hat der Empfänger immerhin einen Hinweis auf die Kodierung. Manche Software weiß dieses UTF-8-BOM aber nicht zu deuten, oder sie hat per HTTP eine abweichende Kodierungsinformation erhalten; sie gibt dann am Anfang oft die Zeichenkette  aus.

Die einzelnen BOM im Überblick
Bytes Kodierung
00 00 FE FF UTF-32, big-endian
FF FE 00 00 UTF-32, little-endian
FE FF UTF-16, big-endian
FF FE UTF-16, little-endian
EF BB BF UTF-8

So ein BOM können wir im Prinzip in allen textbasierenden Dateien verwenden. Allerdings lösen sie oft auch Probleme aus: Wenn wir eine Datei mit BOM in PHP einbinden, bekommen wir schnell die berühmte Fehlermeldung headers already sent, weil das BOM vor dem ersten <?php kommt.
Da U+FEFF ein Leerzeichen ist – überdies ohne Breite – können wir es mit einem einfachen Blick in den Quellcode nur schwer finden. Ein Hex-Editor bringt es zwar ans Licht, aber spätestens dann dürfte auch dem letzten aufgehen, daß eine der anderen Möglichkeiten wohl besser ist.
Siehe auch: BOM entfernen.

Cookies

Wir kennen Cookies aus HTTP als kurze Zeichenketten, die sich der User-Agent merkt. In Programmierskripten gibt es ein encoding cookie. Es sieht meistens so aus:

# -*- coding: utf-8 -*-

Wenn wir das in eine der ersten Zeilen unseres Codes schreiben, erkennen es die meisten Texteditoren und auch einige Interpreter wie beispielsweise IDLE für Python.

PHP

In PHP ist alles ein bißchen komplizierter, weil der Interpreter die Erweiterungen (Extensions) nicht automatisch über die interne Kodierung informiert.
Ich rufe am Anfang meiner PHP-Skripte normalerweise die Funktion set_default_encoding() auf, die ich so definiert habe:

/**
 * Setzt die interne Zeichenkodierung und informiert
 * die Extensions.
 *
 * @author Thomas Scholz <http://toscho.de>
 * @param string $enc Name der Zeichenkodierung
 * @return void
 */
function set_default_encoding($enc = 'UTF-8')
{
    if ( extension_loaded('iconv') )
    {
        iconv_set_encoding('internal_encoding', $enc);
        iconv_set_encoding('input_encoding',    $enc);
        iconv_set_encoding('output_encoding',   $enc);
    }
    if ( extension_loaded('mbstring') )
    {
        mb_internal_encoding($enc);
        if ( 'UTF-8' == strtoupper($enc) )
        {
            mb_language('uni');
        }
    }
    return;
}

Den eigentlichen PHP-Code kann man übrigens getrost in UTF-8 schreiben. Das stört den Interpreter nicht. Er nimmt selbst Funktionsnamen wie ±() hin – was nicht heißt, daß das ein guter Name ist.

Ab Version 6 wird PHP endlich automatisch überall UTF-8 erwarten. Bis dahin müssen wir uns mit den MB-String-Funktionen behelfen. Oder anderen Programmiersprachen, wenn diese Wahl besteht. Siehe auch: UTF-8 mit PHP erzwingen.

7 Kommentare

  1. Frank am 16.01.2009 · 21:44

    Danke, wunderbarer Beitrag.

  2. Peter am 20.06.2009 · 14:56

    маш ли много работа
    имаш ли много работа
    kann mir jemand helfen, wie kann ich diese zeichen übersetzen,

    habe noch mehr davon, stammt aus einem bulgarischen gesprächsprotokoll.

    danke
    peter

  3. Thomas Scholz am 20.06.2009 · 15:22

    @Peter: Das ist ein in UTF-8 kodierter Text, der aber als ISO-8859-1 dargestellt wird. Stelle die Kodierung der Ausgabe um, dann siehst du dieses:

    маш ли много работа имаш ли много работа

    Noch einfacher: Erstelle ein HTML-Dokument, das etwa so aussieht:

    <!doctype html>
    <meta charset="utf-8">
    маш ли много работа
    имаш ли много работа

    Du kannst da den ganzen Text hineinkopieren, den du »entstellt« vorliegen hast.

    Speichere die Datei mit der Zeichenkodierung Windows-1252 (oder ANSI) ab, und rufe es im Browser auf. Der wandelt die Zeichen dann für die Ausgabe korrekt um.

  4. Matilda am 19.09.2009 · 12:15

    Ein Artikel, den man sich selbst schon lange als Spikzettel schreiben wollte, aber zur Ausführung immer zu "bequem" war ;-)

    Super Service - Danke!!

  5. deni am 12.01.2010 · 15:04

    Ab PHP 5.3 kann man folgendes nutzen:

    <?php
    declare(encoding='UTF-8');
    // hier folgt der Code
    ?>

    Entfallen damit die MB-Strings bzw. Deine Funktion?

    Vielen Dank.

  6. Michael am 28.10.2010 · 07:13

    Ich finde es wird erst richtig schwierig, wenn noch MySQL ins Spiel kommt. Dann muss man auch noch mit der richtigen Zeichkodierung die Datenbank ansprechen und mit richtigen Zeichenkodierung das HTML ausgeben. Da bin ich schon einige Male verzweifelt, so dass Zeichen in der Datenbank richtig waren, aber bei der Ausgabe kryptisch dargestellt wurden.
    Aber insgesamt ein toller Artikel zum Einstieg, zur Erleichterung im Umgang mit Zeichenkodierungen.

  7. Tobias H. am 27.04.2011 · 19:51

    @Michael: Um bei MySQL einen Zeichensatz festzulegen, kann man einfach nach dem die Verbindung hergestellt wurde folgenden Query absetzen:

    mysql_query("SET NAMES 'utf8'", $connectionID);

    Hilft bei mir und wird auch sonst empfohlen.