toscho.design

Zeichenkodierung: Be- und Mißgriffe

Zeichenkodierung ist eigentlich kompliziert genug. Viele Autoren verdunkeln das Ganze aber noch durch schlampige Sprache, meistens aufgrund gefährlichen Halbwissens, das sie lieber weitergeben als ergänzen. Also: Licht an!

Charset, Zeichensatz

Ein Begriff, auf den wir zu oft stoßen, ist »Zeichensatz« oder englisch charset. Dieser Ausdruck wird inzwischen in so vielen Bedeutungen gebraucht, daß er überhaupt keine mehr hat. Ich habe bisher folgende erlitten:

  1. Zeichenumfang (also die spezielle Auswahl der Zeichen die beispielsweise in US-ASCII ausgedrückt werden können),
  2. Name der Zeichenkodierung, beispielsweise Latin-1,
  3. a und b zusammen,
  4. Zeichenmenge (Quantität),
  5. Alphabet (deutsches , hebräisches, chinesisches etc.) und
  6. Schriftart. Man möchte weinen.

Das Problem sei an einem Beispiel illustriert: In (X)HTML 4 darf man fast alle Zeichen benutzen, die in der jeweils aktuellen Unicode-Version aufgeführt werden. Und man kann die Zeichenkodierung frei wählen. Allerdings kann nicht jede Zeichenkodierung den ganzen Unicode-Umfang ausdrücken; deshalb gibt es Umwege, zu denen wir gleich kommen.
Welchen »Zeichensatz« hat jetzt also ein HTML-Dokument? Dank der Bedeutungsinflation dieses Ausdruckes können wir diese Frage nicht mehr sicher beantworten.

Also verwenden wir »charset« oder »Zeichensatz« in natürlicher Sprache nicht. Dies empfiehlt auch das W3C den Autoren seiner Spezifikationen im Character Model for the World Wide Web. Siehe auch: Character Set Considered Harmful.

In künstlichen Sprachen meint der Ausdruck meistens die Zeichenkodierung. In CSS etwa schreiben wir in der ersten Zeile:

@charset "utf-8";

Maskierung: Entity und Zeichenreferenz

Wir sprachen gerade von einem Umweg, auf dem wir doch alle Unicode-Zeichen ausdrücken können: Wenn wir kein UTF-8 verwenden und Zeichen außerhalb des Zeichenumfangs der aktuellen Kodierung verwenden wollen, dann müssen wir sie maskieren. In (X)HTML immer: & und <, je nach Kontext auch >, " und ' – unabhängig von der Zeichenkodierung.

Diese Maskierung können wir auf zweierlei Weise erreichen:

  1. Entities: &amp;, &lt;, &gt; &apos; und &quot;. Diese fünf kennt jeder XML-Parser.
  2. Numerische Zeichenreferenzen: Das sind keine Entities.
    Sie beziehen sich immer auf die numerische Position im Unicode-Standard. Diese Position ist eine hexadezimale Zahl, sie kann aber auch im Dezimalsystem ausgedrückt werden. In der Hexadezimalschreibweise steht die Zahl zwischen &#x und ;, in der dezimalen nur zwischen &# und ;.

Beispiel: Wir wollen eine Anleitung für eine Patience schreiben. Wir brauchen das Zeichen ♠, aber das Dokument ist in ISO-8859-1 kodiert, oder der Editor kann kein UTF-8. Dann können wir das Zeichen in drei Schreibweisen ausdrücken:

  1. Als Entity: &spades.
  2. Als hexadezimale Zeichenreferenz: &#x2660;.
  3. Als dezimale Zeichenreferenz: &#9824;.

Dezimale Maskierung in der Praxis

Allgemein wird die Dezimalschreibweise in (X)HTML am besten unterstützt. Wir geben ihr den Vorrang vor der nicht maskierten Schreibweise, wenn wir das Zeichen sonst schwer oder nicht erkennen.
Einige Beispiele: Das geschützte Leerzeichen U+00A0 sieht im Quelltext genauso aus wie das ungeschützte. Schreiben wir es als &#160;, dann werden wir es nie verwechseln oder übersehen. Ähnliches gilt für U+200F, das arabische oder hebräische Zeichen von rechts nach links laufen läßt: Im Quellcode ist es unsichtbar, deshalb schreiben wir es als &#8207;.

Entities lassen sich zwar im Code besser lesen; sie sind aber nicht überall vollständig implementiert. Definiert werden sie in HTML 4 und XHTML in separaten Dokumenten; XHTML-Parser müssen diese aber nicht »kennen« – und manche tun das auch nicht – sie müssen nur die fünf für XML allgemein bekannten Entities auflösen.
Wir verwenden in XHTML und XML (in Newsfeeds beispielsweise) deshalb keine Entities.

Und wir setzen immer das Semikolon ans Ende der Maskierung. In HTML ist das in einigen Fällen optional, weshalb dann auch keine Warnung vom Validator kommt, aber die meisten Browser möchten gerne eins, und es gilt als guter Stil.

Andere Sprachen

In allen anderen Sprachen benutzen wir Hexadezimalreferenzen.
In CSS können wir U+2660 als »generated content« so einfügen:

.spades::before {
    content:    "\002660";
}

Zeichen und Glyphe

Diese beiden Begriffe werden oft verwechselt: Das Zeichen ist die referenzierte Unicode-Position, die Glyphe das Aussehen in einer bestimmten Schrift.

Vergleichen wir beispielsweise das Zeichen »a« (U+0061) in Georgia und Comic Sans:


Schriftvergleich

Schriftvergleich


Beide Schriften habe sehr unterschiedliche Glyphen für dasselbe Zeichen.

Echte und falsche Smileys

In der Windows-Schrift »Wingdings« ist für U+004A (das große J) als Glyphe das Smiley ☺ , das eigentlich auf U+263A zu Hause ist. Schlecht informierte Autoren oder noch schlechter programmierte Editoren schreiben dann so etwas in den Code, um einen Smiley zu erzeugen:

<font face="Wingdings">J</font>

Der Nachteil ist offensichtlich: Wer diese Schrift nicht hat oder einen korrekt arbeitenden Browser benutzt, der in Wingdings kein J findet und es deshalb aus einer anderen Schrift holt, sieht eben nur ein J und kein ☺. Nicht lustig.

Beziehungen

Zwischen Glyphe und Zeichen besteht keine Eins-zu-eins-Beziehung: Mehrere Zeichen können durch eine Glyphe repräsentiert werden, beispielsweise in Ligaturen wie œ. Andererseits können mehrere Glyphen gebraucht werden, um ein Zeichen darzustellen.

Obwohl also der technische Zusammenhang zwischen Zeichen und Glyphe sehr lose ist, gibt es Bindungen: Einige Browser holen sich Zeichen aus anderen Schriften, wenn die aktuell benutzte nicht gut genug ausgestattet ist. Andere nicht. Dieses Verfahren wird Glyphen-Substitution genannt, und nicht alle Browser beherrschen es gleichermaßen gut, wie das Beispiel mit den Smilies zeigt.
Deshalb testen wir unsere Webseiten besser mit möglichst vielen und realen Texten.

3 Kommentare

  1. TJ am 13.09.2010 · 09:43

    Danke für die vollständige "Definition" von Zeichensatz
    und die Empfehlung, es nicht im Gespräch zu verwenden.
    Das hilft wirklich!

  2. Elec am 18.08.2011 · 10:31

    Entities lassen sich zwar im Code besser lesen; sie sind aber nicht überall vollständig implementiert. In XHTML werden sie nicht direkt in der DTD benannt, sondern »nachgeladen«. XHTML-Nutzeragenten brauchen sich darum nicht zu kümmern und müssen deshalb nur die fünf für XML allgemein bekannten Entities auflösen. Alle anderen sind optional und werden nicht zuverlässig unterstützt.
    Wir verwenden in XHTML und XML (in Newsfeeds beispielsweise) keine Entities.

    Ich finde den markierten Satz unklar formuliert. (Obwohl - oder weil - ich nun schon die zweite Tasse Kaffee trinke.)

    Wieso müssen sich XHTML-Nutzeragenten (Gemeint sind Clients, zum beispiel Web-Browser, die XHTML interpretieren können?), wieso müssen sich XHTML-Nutzeragenten nicht darum kümmern? Weil die Entitie-Definitionen automatisch nachgeladen werden? Wenn das so ist, warum müssen dann diese Nutzeragenten die fünf bekannten Entities auflösen? Ich hatte es so verstanden, dass die fünf am ehesten bekannt sind. Soll damit der Anspruch an einen XHTML-Nutzeragenten ausgedrückt werden, dass er ohne Nachladen mindestens diese fünf Entities auflösen können soll?

    Ich würde empfehlen, diesen Abschnitt unmißverständlicher und konkreter zu formulieren.

    Regards
    Elec

  3. Thomas Scholz am 18.08.2011 · 12:45

    @Elec: Stimmt, guter Hinweis, danke. Das war deshalb so schlecht formuliert, weil es schlicht falsch war. Ich habe es etwas verbessert.