Zeichencodierung/Unicode in der Praxis

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche
Sie haben also gehört, dass es sinnvoll ist, Unicode (UTF-8) für Ihre Seiten zu verwenden, anstatt eine alte Zeichenkodierung wie Latin1 (Windows 1252 oder ISO 8859-1) oder Shift_JIS.

In diesem Tutorial erfahren Sie, wie Sie die Zeichenkodierung Ihrer Webseite auf UTF-8 ändern können.[1]

Ratschlag zur praktischen Anwendung

Empfehlung: Verwenden Sie, wo immer es geht, UTF-8! Es hat viele Vorteile, bei der Wahl der codierbaren Zeichen nicht eingeschränkt zu sein. Selbst wenn ein Webauftritt garantiert nur für eine einzige Sprache (z. B. Deutsch) gestaltet werden muss, bietet nur Unicode die komplette Freiheit, wirklich alle Zeichen – insbesondere Interpunktionszeichen wie „typografische Anführungszeichen“, das Eurozeichen, wissenschaftliche Symbole oder Emojis – beliebig verwenden zu können. Eingestreute Fremdwörter in einer anderen Sprache sind genauso problemlos möglich wie die Verwendung eher exotischerer Zeichen wie den Brüchen ⅓, ⅔, ⅛, ⅜, ⅝, ⅞.

Fehlerdiagnose

Die häufigsten Fehler sind als ISO-8859-1, -15 oder Windows-1252 deklarierte Daten, die aber tatsächlich UTF-8-kodiert wurden oder umgekehrt. Dies äußert sich dann darin, dass beispielsweise aus einem ä die Zeichenfolge À wird – das Zeichen ä wird in UTF-8 in zwei Bytes kodiert, was in Ein-Byte-Codierungen zwei Zeichen entspricht. Tritt hingegen das Zeichen auf, werden Daten in einer Ein-Byte-Codierung (ISO-8859-X, Windows-1252) als UTF-8 deklariert und interpretiert – nicht alle Byte-Kombinationen aus Ein-Byte-Codierungen sind gültiges UTF-8, und das wird durch das spezielle Zeichen kenntlich gemacht.

In beiden Fällen muss nach der abweichenden Zeichencodierungs-Deklaration im System gesucht werden und an die tatsächliche angepasst werden – wobei ein Umstieg auf UTF-8 bevorzugt werden sollte.

Grundsätzlich besteht auch das Problem, dass Ein-Byte-Zeichencodierungen ohne weiteres Wissen (z. B. eine BOM) nicht voneinander unterschieden werden können – UTF-8-kodierter Text, der nur den Zeichensatz von ASCII beinhaltet, ist ebenso auch gültiges ISO-8859-1. Daher wird beispielsweise ein Texteditor bei einer Datei, die nur als gültiges UTF-8 interpretierbare Bytes enthält, auch UTF-8 als Codierung annehmen. Andernfalls wird er eine ISO-8859-Variante wählen. Dieses Verhalten ist allerdings keinesfalls standardisiert oder zwischen verschiedenen Systemen identisch, es ist lediglich eine schlüssige Vorgehensweise.

Unicode-Abdeckung einer Schriftart

Nicht alle Schriftarten decken Unicode komplett ab, sodass die Möglichkeit, fast alle Zeichen der Welt kodieren zu können, noch nicht bedeutet, sie auch ohne Weiteres darstellen zu können. Eine Schriftart oder -familie sollte auch danach ausgewählt werden, ob sie die benötigten Zeichen beinhaltet. Wird eine Schriftfamilie als Ausweichlösung benötigt, die möglichst viel abdeckt, so ist GNU Unifont einen Blick wert.

Umsetzung

Speichern Sie die Daten als UTF-8

Es reicht nicht aus, die Deklarationen innerhalb Ihrer Seiten zu ändern, um anzugeben, dass die Seite in UTF-8 kodiert ist. Sie müssen sicherstellen, dass Ihre Daten tatsächlich in UTF-8 kodiert, d. h. gespeichert sind.

Screenshot einer Webseite, die in utf-8 kodiert wird.

Wenn Sie mit manuell bearbeiteten Dateien arbeiten, sollten Sie die Optionen Ihres Editors verwenden, um die Datei in UTF-8 zu speichern und nicht in der Kodierung, die Sie bisher verwendet haben. Wenn Sie Dateien aus Skripten und Datenbanken erstellen, sollten Sie sicherstellen, dass die Daten bei Bedarf konvertiert und die richtigen Parameter in Ihrer Skripting-Umgebung gesetzt werden.

Beachten Sie, dass Sie möglicherweise sicherstellen müssen, dass die Daten keine UTF-8-Signatur, auch bekannt als Byte-Order-Mark (BOM), enthalten.

Deklarieren Sie die Kodierung in Ihrer Seite

Sie sollten die Zeichenkodierungsdeklaration in Ihrer Seite ändern (oder eine hinzufügen, wenn Sie sie nicht bereits deklarieren).

In ihrer einfachsten Form sieht diese Erklärung wie folgt aus und sollte im Grundgerüst am Anfang des head-Elements in Ihrem HTML-Code stehen.

Beispiel
<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Titel der Seite | Name der Website</title>
  </head>
...
Beachten Sie: Die Deklaration <meta charset="utf-8"> gibt nur an, dass die Datei in utf8 gespeichert wäre. Falls Sie Schritt 1 nicht erledigt haben, bleibt sie wirkungslos!

Die Meta-Angabe wird ebenfalls genutzt, wenn die HTML-Datei nicht per HTTP empfangen wurde, sondern beispielsweise direkt von der Festplatte gelesen wurde. Es ist also in jedem Fall sinnvoll, die Meta-Angabe unbedingt in die HTML-Datei einzufügen.

Stellen Sie sicher, dass Ihr Server das Richtige tut

Auch wenn Ihre Daten in UTF-8 vorliegen und Sie dies auf der Seite angegeben haben, kann es sein, dass Ihr Server die Seite mit einem HTTP-Header ausliefert, der besagt, dass es sich um etwas anderes handelt.

Testen Sie es, indem Sie die URL Ihrer Seite im Internationalization Checker überprüfen. Suchen Sie in der Tabelle nach der Zeile mit dem Titel HTTP Content-Type unter Character Encoding und überprüfen Sie, ob dort entweder UTF-8 oder No encoding information found steht.

Wenn der HTTP Content-Type eine andere Kodierung als UTF-8 anzeigt, müssen Sie Maßnahmen ergreifen, um dies zu korrigieren, da die Deklaration im HTTP-Header die Informationen innerhalb der Seite außer Kraft setzt

Um die Kodierung im HTTP-Header zu ändern, sind Server-Administratorrechte erforderlich. Sie können dies jedoch auch selbst tun, wenn Sie Dateien über einen ISP bereitstellen. Wenden Sie sich an Ihren Server-Administrator. [2]

Erkennung der Codierung von Webdokumenten

Wenn ein Browser ein Webdokument anfordert, „sieht“ er im Prinzip nur eine Folge von Bytes, aus denen er entnehmen muss, für welche Zeichen sie stehen. Er hat dazu mehrere Möglichkeiten, die verwendete Codierung eines Webdokuments zu erkennen.

HTTP-Header

Der ideale Fall ist, wenn sie ihm explizit mitgeteilt wird. Natürlich muss die Angabe einer Codierung auch mit der tatsächlich verwendeten Codierung übereinstimmen. Für Webdokumente auf Webservern, auf die per HTTP zugegriffen wird, ist vorrangig vor allen sonstigen Angaben die Auszeichnung im HTTP-Header Content-Type maßgeblich. Das sieht für HTML-Dokumente so aus:

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

Diese Zeile ist allerdings für den uneingeweihten Browsernutzer unsichtbar, da sie Bestandteil der HTTP-Kommunikation ist und von Browsern in der Regel nicht angezeigt wird. (Zum Sichtbarmachen siehe Tools)

Wird ein Dokument als UTF-16 ausgeliefert (z. B. da dieses bei asiatischen Websites mit eher wenigen Tags effizienter als UTF-8 ist), ist der Content-Type im HTTP-Header zwingend korrekt zu setzen, da der Browser das Dokument sonst wegen der Struktur von UTF-16 für eine Binärdatei hält und es herunterladen möchte anstatt es anzeigen.

HTML-Dokumente

Wenn der Browser per HTTP keine Angabe zum charset empfängt, kann hilfsweise ein HTML-Meta-Element verwendet werden, um dieselbe Angabe via HTML nachzureichen:

<meta charset="utf-8">

Die Meta-Angabe wird ebenfalls genutzt, wenn die HTML-Datei nicht per HTTP empfangen wurde, sondern beispielsweise direkt von der Festplatte gelesen wurde. Es ist also in jedem Fall sinnvoll, die Meta-Angabe unbedingt in die HTML-Datei einzufügen. Mancher Webserver liest vor der Auslieferung die HTML-Datei ein und setzt die HTTP-Header auf die Werte, die er in den Meta-Elementen findet.

Sollte weder im HTTP-Header noch im Meta-Element eine Angabe zur Codierung gefunden werden, sowie in HTML5-Dokumenten keine BOM vorhanden sein, wendet der Browser ein Ratesystem an (zum Beispiel anhand der Länderdomain), mit dem die wahrscheinlichste verwendete Codierung automatisch ermittelt wird. Dieser Vorgang liefert oftmals korrekte Werte, in exotischen Fällen kann er aber auch falsch liegen. Als letzte Möglichkeit kann der Benutzer dann auch manuell eingreifen und über einen Menüpunkt „Zeichencodierung“ (meist im Menü „Ansicht“ zu finden) manuell eine Auswahl treffen, bzw. er muss eher ein wenig herumprobieren, welche der vielen Wahlmöglichkeiten ihm eine korrekte Darstellung liefert.

CSS-Dokumente

Die @charset-Regel erlaubt Ihnen zu Beginn des CSS-Dokuments die Definition der Zeichencodierung. Wenn Sie keine Regel aufführen, wird die Angabe im HTTP-Header oder die Zeichencodierung des einbindenden Dokuments angewendet.

Beispiel
@charset "UTF-8"; @media screen{ ... } @media print{ ... }
Die @charset Angabe sollte allen weiteren Angaben vorangehen.

Diese Angabe ist optional. Wenn sie aber vorhanden ist, muss sie als allererstes im Dokument stehen.

Sie können in CSS jedoch jedes Unicode-Zeichen direkt adressieren durch die Form \nnnn. n stellt hierbei den HEX-Wert der Unicode-Zeichennummer dar.

JavaScript

JavaScript kennt keine Möglichkeit der Angabe einer Codierung. Hier bleibt nur der Content-Type-HTTP-Header. Bei in HTML-Dokumenten eingebettetem JavaScript-Code gilt die Codierung des HTML-Dokuments.

Verwendung von beliebigen Zeichen

Jede Sprache (Programmiersprachen wie Auszeichnungssprachen) hat mehr oder weniger eigene Regeln zur Notation von Zeichen-Alternativen, wenn sich Zeichen nicht direkt einfügen lassen. Für Programmiersprachen lassen sich diese Regeln in der jeweiligen Dokumentation in den Kapiteln zur Notation von Strings/Stringliteralen finden. Zu beachten sind Verschachtelungen, beispielsweise JavaScript-Code in <script>-Bereichen oder CSS in <style>-Bereichen in HTML-Dokumenten. Hier gelten nicht die nachfolgend genannten Regeln für HTML sondern die von JavaScript beziehungsweise CSS. Ein Attributwert eines HTML-Elements hingegen folgt den HTML-Regeln, auch wenn es sich um ein Eventhandler- oder style-Attribut handelt. Allerdings wird der Inhalt dieser Attributewerte anschließend von den jeweiligen Systemen interpretiert, weswegen auch deren Schreibweise verwendet werden kann. Damit sind an dieser Stelle zwei Schreibweisen möglich. Das W3C empfiehlt, die systemeigene statt der HTML-Schreibweise zu verwenden.

HTML

Wenn ein HTML-Dokument nicht in UTF-8 geschrieben werden kann, aber dennoch Zeichen verwendet werden müssen, die sich mit der gewählten Codierung nicht direkt darstellen lassen, gibt es zwei Ausweichmethoden, um die Zeichen trotzdem ins Dokument einzufügen. Da HTML als Zeichenumfang eines Dokuments (Zeichensatz) grundsätzlich den ganzen Unicode-Bereich erlaubt und die Browser daher grundsätzlich fähig sind, alle Unicode-Zeichen auszugeben (sofern die aktivierte Schriftartdatei für das Zeichen eine Malanweisung enthält), sind diese Alternativmethoden lediglich umständlicher in der Behandlung.

Empfehlung: Verwenden Sie, wann immer es geht, die Zeichen direkt und nicht ihre Ersatzschreibweise. Eine Ausnahme sind nicht sichtbare Zeichen (z. B. geschützte Leerzeichen oder bedingte Trennstriche) bzw. schwer unterscheidbare Zeichen (z. B. Binde- und Gedankenstriche sowie Minus-Zeichen), welche Sie zur besseren Erkennbarkeit als Entity oder Numeric Character Reference (NCR) notieren sollten.
Beachten Sie: dass Sie die HTML-eigenen Zeichen <, >, & sowie " und ' in Attributwerten unabhängig von der gewählten Codierung als Entity oder NCR schreiben müssen.

Die erste Methode ist, ein Zeichen als sogenannte Entity einzufügen. Entity (Entität, Einheit, Gebilde) oder genauer Character Entity Reference (Zeichen-Entität-Referenz) ist der Fachbegriff für „benanntes Zeichen“. Anstatt das gewünschte Zeichen direkt einzugeben, schreibt man als Alternative den Zeichennamen, umrahmt von der Entity-Markierung & und ;. Beispiele: Das große Ä (A-Umlaut) wird als Entity &Auml; geschrieben, das kleine ä als &auml; und das Eurozeichen ist &euro;. Die Groß-/Kleinschreibung ist dabei von Bedeutung.

Die zweite Methode ist, ein Zeichen als numerische Zeichenreferenz zu notieren. Hierbei wird der Unicode-Codepoint in dezimaler oder hexadezimaler Schreibweise verwendet. NCRs werden mit &# eingeleitet, gefolgt von x, wenn der Codepoint als Hexadezimalzahl folgt und mit ; abgeschlossen.

Hexadezimal: &#x20AC;
Dezimal:     &#8364;

Die hexadezimale Notation bietet für Menschen den Vorteil, dass ein Zeichen in den Codetabellen besser gefunden werden kann, denn die Organisation der Zeichen in Unicode-Blöcken orientiert sich an den Hex-Werten.

Eine Vielzahl benannter Zeichen und ihrer NCRs sind in der Zeichenreferenz aufgeführt.

Javascript

Zeichen, die in der Codierung eines Dokuments nicht direkt verwendbar sind, können über die Escape-Sequenz \uxxxx eingefügt werden. Statt der xxxx ist vierstellig, gegebenenfalls mit führenden Nullen, der UTF-16BE- oder UCS-2BE-Code in hexadezimaler Schreibweise anzugeben, was mit 4-stelligen Unicode-Codepoints identisch ist. Die Kodierung des Eurozeichens, U+20AC, ist vierstellig und daher sehr einfach:

\u20AC

Soll ein 5- oder 6-stelliger Unicode-Codepoint eingebunden werden, muss er zunächst in UTF-16BE-Surrogates umgewandelt und diese kodiert werden. Aus dem langen I aus dem Mormonenalphabet, U+10400, wird somit:

\uD801\uDC00

In einigen mit JavaScript verwandten Programmiersprachen existiert die Escape-Sequenz \Uxxxxxxxx, wobei statt xxxxxxxx der Unicode-Codepoint steht, der mit Nullen auf 8 Stellen aufgefüllt wird (man spricht von UTF-32BE oder UCS-4BE). Diese funktioniert in JavaScript nicht.

CSS

CSS kennt drei Arten der Escape-Schreibweise.

\xxx...

Statt der xxx steht der Unicode-Codepoint in hexadezimaler Schreibweise. Führende Nullen sind nicht notwendig. Folgt anschließend ein anderes Zeichen als 0-9, a-f oder A-F, so kann dieses direkt anschließend notiert werden. Anderenfalls muss ein Leerzeichen (oder anderes Whitespace-Zeichen) folgen, welches zur Escape-Sequenz gehört und nicht weiter beachtet wird. Ein Zeilenumbruch bestehend aus CR und LF wird in dem Fall jedoch als ein Whitespace-Zeichen angesehen.

\xxx ...

Außerdem kann eine hexadezimale Zahl 6-stellig mit führenden Nullen notiert werden.

\000xxx...
Beachten Sie, dass bei allen drei Arten ein der Zahl folgendes Whitespace-Zeichen als Teil der Escape-Sequenz angesehen und „verschluckt“ wird.

Es folgen noch einmal alle drei Schreibweisen als konkrete Beispiele.

émotion: \E9motion
édition: \E9 dition  –  \E9dition ergäbe ຝition
édition: \0000E9dition

Webserver

In Richtung Client

Ein Webserver teilt einem Client (z. B. Browser) in einem HTTP-Header namens Content-Type (Content = Inhalt) mit, um was für Daten es sich in einer Response auf einen Request handelt. Für alle Text-Dokumente, inklusive HTML-, CSS- und JavaScript-Dokumente, beginnt der Media-Type mit text/... und es kann der optionale Parameter charset mitgegeben werden.

Das Senden von HTTP-Headern kann man auf vielfältige Weise veranlassen. Sie können im Webserver konfiguriert werden und/oder von eingebetteten Systemen (beispielsweise PHP) explizit angegeben werden. Lesen Sie dazu bitte in den Abschnitten der jeweils verwendeten Systeme nach oder in der systemspezifischen Dokumentation.

HTTP-Response-Header für ein HTML-Dokument
HTTP/1.1 200 OK Date: Fri, 26 Mar 2010 10:25:40 GMT Content-Length: 42 Content-Type: text/html; charset=utf-8 Last-Modified: Fri, 26 Mar 2010 10:01:43 GMT <html>...

Nicht immer stehen HTTP-Header zur Verfügung, beispielsweise dann nicht, wenn Dokumente lokal abgespeichert wurden. Unter anderem für diesen Fall kann eine charset-Angabe auch in einem meta-Element stehen. Dieses muss in den ersten 1024 Bytes eines Dokuments stehen, also sollte es ziemlich am Anfang des head-Elements platziert werden.

HTML5-Dokument
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Beispiel</title> </head> <body> ... </body> </html>
Beachten Sie: Wenn mehrere Angaben vorhanden sind, gilt diese Rangfolge:
  1. HTTP-Header Content-Type
  2. XML-Deklaration (bei XHTML)
  3. Meta-Element


Für CSS-Dokumente gilt:

  1. HTTP-Header Content-Type
  2. @charset-Regel im Dokument
  3. charset-Attribut im link-Element
Beachten Sie außerdem, dass Browser zu raten anfangen, wenn sie keine Angabe zur Zeichencodierung finden. Das Ergebnis ist unvorhersehbar und mitunter sicherheitskritisch. (Suchen Sie nach „utf-7 security“, wenn Sie mehr dazu wissen wollen.) Durch Angabe einer korrekten Codierungsinformation lassen sich diese Probleme vermeiden.

In Richtung Server

POST

Ein Formular wird üblicherweise von den Browsern in der Codierung versendet, die für die Seite angegeben wurde, in dem es steht. Wenn also der Server eine Webseite mit einem Formular ausliefert, die in UTF-8 codiert ist, wird der (Antwort-)Request mit den Formulardaten ebenfalls UTF-8-codiert werden.

Für das form-Element existiert auch das accept-charset-Attribut, in dem Sie eine Liste von Zeichencodierungen angeben können. Es ist jedoch empfehlenswert nur eine einzige Angabe zu machen. Es gibt keine Regel oder irgendein etabliertes Verfahren, mit der/dem ein Client dem Server die verwendete Codierung mitteilen kann. Der Server ist deshalb darauf angewiesen, dass der Client „von selbst“ die gewünschte Codierung verwendet. Wenn für accept-charset nun mehr als ein Wert angegeben wird, kann sich der Client irgendeinen raussuchen und der Server bekommt nicht mitgeteilt welcher es ist. Und es gibt keine Methode, die mit absoluter Sicherheit in der Lage wäre, allein aus Daten eine Codierung zu erkennen. Weiterhin ist zu beachten, dass nicht alle Browser dieses Attribut korrekt auswerten und sich daran halten. Man kann sich also nicht darauf verlassen, weswegen man es im Prinzip auch gleich weglassen kann.

Ein weiteres Problem entsteht, wenn Sie eine Codierung für einen begrenzten Zeichensatz verwendet haben, beispielsweise ISO-8859-1, und der Anwender Zeichen eingegeben hat, die mit dieser Codierung nicht darstellbar sind. Auch für diesen Fall gibt es keine einheitliche Regel, wie der Browser zu verfahren hat. Einige Browser senden statt dieser Zeichen eine Numerische Zeichenreferenz. Das Problem dabei ist, dass serverseitig nicht unterschieden werden kann, ob ein & nun vom Anwender eingegeben wurde oder ob der Browser es für die Umschreibung eines Zeichen eingefügt hat. Mit UTF-8 hingegen sind Sie auf der sicheren Seite.

Nachdem nun die eingegebenen Zeichen vom Browser in eine bestimmte Codierung gebracht wurden, werden sie nun noch URL-codiert. Auf dem Server muss also zunächst eine URL-Decodierung erfolgen. Als PHP-Anwender muss man selbst nichts unternehmen, denn in Servervariablen ($_POST, $_GET) stehen bereits die decodierten Daten.

POST
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Beispiel-Formular</title> </head> <body> <form method="post" action=""> <input type="text" name="foo" value="Umlaut-Ärger"> <input type="submit"> </form> </body> </html>
Oben sehen Sie ein Formular und unten den vom Browser erzeugten (etwas gekürzten) Request, wenn man es absendet.
POST /test.php HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 ... Content-Type: application/x-www-form-urlencoded Content-Length: 21 foo=Umlaut-%C3%84rger
Das Ä von „Umlaut-Ärger“ wurde UTF-8-codiert (C3 84) und anschließend URL-codiert (%C3 %84).

URL, inklusive GET

In einer URL gibt es zwei Bereiche, in denen Daten transportiert werden können. Das ist zum einen der Path und zum anderen der Querystring.

Ein Querystring ist entweder in Links vorhanden oder wird vom Browser bei einem Formular mit method="GET" erstellt. Für ein Formular gilt grundsätzlich auch das im obigen Abschnitt zu POST gesagte. Aus der href-Angabe in Links wird hingegen (zumindest von Firefox (3.6) und IE8) direkt die Request-URL gebildet, ohne eine URL-Codierung vorzunehmen.

Interessant wird die Geschichte erst, wenn es keine Webseite mit einer definierten Codierungsangabe gibt, in der ein anklickbarer Link steht, also wenn man zum Beispiel eine URL händisch in die Adress-Zeile des Browsers eingibt, sie aus einer Textdatei kopiert oder ein externes Programm dem Browser eine URL übergibt. In dem Fall hat der Browser keine Response und deren Zeichencodierungsangabe vorliegen, er muss sich also bei einer Default-Konfiguration bedienen. Hier verhalten sich die Browser durchaus unterschiedlich, und verwenden teilweise auch verschiedene Codierungen für Path und Querystring. Doch das ist ein Thema für den Abschnitt Zeichencodierung - Browser.

Tools

Um lediglich die Angaben zur Zeichencodierung zu prüfen, kann man den W3C Internationalization Checker verwenden. Die kompletten HTTP-Header lassen sich einerseits mit einen Netzwerksniffer à la Wireshark ansehen, doch leichter zu bedienen sind der Seiteninspektor in Firefox, bzw. die Developer Tools in Chrome und Edge.

Weblinks

  1. W3C: Changing an HTML page to Unicode
  2. W3C: Einstellung der Zeichencodierungsangabe ('charset') in .htaccess