Was bedeutet der UTF-8 Zeichensatz?

 

Ein Foto in Ihrer Galerie trägt die Beschreibung „Blick über den Möhnesee“. Das sind 23 Zeichen. Auf der Festplatte des Servers belegt der Text aber 25 Byte, weil „ü“ und „ö“ je zwei Byte brauchen. Die Regel, wie aus Zeichen Bytes werden, legt der UTF-8-Zeichensatz fest. Sie ist der Grund, warum Umlaute, Eurozeichen und Emojis auf derselben Seite nebeneinander stehen können, und auch der Grund, warum aus einem „ü“ manchmal ein „ü“ wird. Wer die Rechnung einmal verstanden hat, liest Fehlermeldungen und Datenbankgrenzen mit anderen Augen.

UTF-8-Zeichensatz in Bytes: A = 41, ü = C3 BC, € = E2 82 AC, Kamera-Emoji = F0 9F 93 B7, also 1 bis 4 Byte je Zeichen

Was der UTF-8-Zeichensatz mit Bytes macht

Hinter UTF-8 stecken zwei Schritte. Zuerst gibt der Standard Unicode jedem Zeichen der Welt eine feste Nummer: Das große A hat die Nummer 65, das „ü“ die 252, das Eurozeichen 8364 und das Kamera-Emoji 128247. UTF-8 legt dann fest, wie diese Nummer als Folge von Bytes gespeichert wird. Kleine Nummern kommen mit einem Byte aus, große brauchen bis zu vier.

Genau genommen ist UTF-8 deshalb kein Zeichensatz, sondern eine Kodierung: Den Vorrat an Zeichen liefert Unicode, UTF-8 ist die Vorschrift, wie man ihn in Bytes schreibt. Im Alltag verschwimmt der Unterschied. Editoren, HTML (charset) und MySQL („Zeichensatz“, englisch character set) sagen zu beidem Zeichensatz, und so hält es auch diese Seite. Für die Praxis ergibt die Kodierung vier Gruppen:

ZeichenBytes in UTF-8 (hexadezimal)Länge
A, 7, Leerzeichen, Bindestrich41, 37, 20, 2D1 Byte
ü, ö, ä, ßC3 BC, C3 B6, C3 A4, C3 9F2 Byte
EurozeichenE2 82 AC3 Byte
Kamera-EmojiF0 9F 93 B74 Byte

Am ersten Byte erkennt ein Programm, wie lang das Zeichen ist. Dafür gibt es feste Bereiche: Ein Byte von 00 bis 7F ist schon das ganze Zeichen. Ein Startbyte von C2 bis DF kündigt ein weiteres Byte an, eines von E0 bis EF zwei und eines von F0 bis F4 drei. Die Startbytes aus der Tabelle, C3, E2 und F0, sind also nur je ein Vertreter ihres Bereichs. Folgebytes liegen immer zwischen 80 und BF. Diese strenge Bauweise macht Fehler sichtbar: Ein Byte wie FC, das „ü“ im älteren latin1, darf in UTF-8 an keiner Stelle stehen. Ein Browser, der UTF-8 erwartet, setzt dafür eine Raute mit Fragezeichen. Umgekehrt liest ein Programm, das latin1 erwartet, die zwei Bytes C3 BC als zwei getrennte Zeichen, „Ó und „¼“.

Wie die vier Gruppen im Speicher aussehen, zeigt das Diagramm. Die Bytes eines Zeichens erscheinen dort nacheinander, so wie ein Programm sie liest. Die Balken rechts zeigen, bis zu welcher Zeile ein Zeichensatz reicht: Was darunter liegt, kann er nicht speichern. ISO-8859-1 und MySQL-latin1 stehen dort getrennt, weil sie sich genau beim Eurozeichen unterscheiden.

Animiertes Diagramm zum UTF-8-Zeichensatz: A belegt ein Byte, ü zwei, das Eurozeichen drei und ein Emoji vier Byte; Balken zeigen, dass ISO-8859-1 nur A und ü kennt, MySQL-latin1 zusätzlich das Eurozeichen, MySQL-utf8 bis drei Byte reicht und erst utf8mb4 alles speichert
Der UTF-8-Zeichensatz in Bytes: Das erste Byte sagt, wie viele noch folgen, und die Länge entscheidet, welcher Zeichensatz das Zeichen aufnehmen kann.

Zeichen sind nicht Bytes: was daraus folgt

Für den Galeriebetrieb folgt aus dieser Bauweise vor allem eins: Länge in Zeichen und Größe in Byte sind zwei verschiedene Zahlen. „Sonnenuntergang am Strand“ hat 25 Zeichen und 25 Byte, denn darin steht kein einziger Umlaut. „Grüße aus Würzburg“ hat 18 Zeichen, aber 21 Byte. Eine Beschreibung aus 500 Zeichen belegt also irgendwo zwischen 500 Byte, wenn sie nur aus einfachen Buchstaben besteht, und 2.000 Byte, wenn sie ausschließlich aus Emojis bestünde. Programme, die Byte zählen statt Zeichen, schneiden Texte mit Umlauten deshalb früher ab, oder schlimmer: mitten in einem Zeichen. Dann bleibt ein einzelnes C3 übrig, und am Ende der Bildunterschrift steht eine Raute.

Auch Datenbanken rechnen an einer Stelle in Byte statt in Zeichen, nämlich beim Index. Was das für die Galerie bedeutet, steht weiter unten beim Schlüssel der Tabelle bilder.

Warum der UTF-8-Zeichensatz latin1 abgelöst hat

Der ältere Zeichensatz latin1, genauer ISO-8859-1, kennt genau 256 Zeichen, jedes ein Byte. Deutsch passt hinein, das Eurozeichen nicht, denn die Norm ist älter als der Euro. Microsoft hat das Eurozeichen in seiner Variante Windows-1252 auf einen frei gebliebenen Platz gesetzt, das Byte 80. Genau diese Variante steckt hinter dem Zeichensatz, den MySQL „latin1“ nennt. In einer alten latin1-Datenbank lässt sich das Eurozeichen also durchaus speichern. Polnische, tschechische oder türkische Buchstaben fehlen dagegen in beiden Fassungen. Wer in einer Galerie Fotos aus dem Urlaub in Gdańsk beschreiben will, kommt mit latin1 nicht weit. UTF-8 deckt alle Zeichen ab, die Unicode kennt, und hat dabei einen klugen Vorteil: Die ersten 128 Zeichen, also die Buchstaben ohne Akzent, Ziffern und gängigen Satzzeichen, sind Byte für Byte identisch mit dem uralten ASCII. Eine Datei, die nur solche Zeichen enthält, ist gleichzeitig gültiges ASCII, gültiges latin1 und gültiges UTF-8.

Genau das nutzen viele Programme bei Dateinamen. Ein Name wie kuerbisse-vor-der-scheune.jpg sieht auf jedem Server, in jedem FTP-Programm und in jeder Adresse gleich aus, egal welcher Zeichensatz dort eingestellt ist. Ein Name mit „ü“ dagegen kann je nach Betriebssystem als zwei verschiedene Bytefolgen ankommen: Windows-Programme schicken ihn manchmal in Windows-1252, und macOS zerlegt das „ü“ gern in ein „u“ und zwei Pünktchen als eigenes Zeichen.

Das BOM: drei Bytes, die niemand sieht

Einige Editoren schreiben an den Anfang einer UTF-8-Datei die Bytes EF BB BF, das sogenannte Byte Order Mark. Im Editor ist davon nichts zu sehen. Steht eine PHP-Datei mit BOM auf dem Server, gibt PHP diese drei Bytes aus, bevor das Script überhaupt startet. Danach kann es keine Kopfzeilen mehr senden, und es erscheint die Warnung „Cannot modify header information - headers already sent“. Bei HTML-Vorlagen sieht man manchmal eine leere Zeile oder ein einzelnes Sonderzeichen ganz oben auf der Seite. Speichern Sie Dateien deshalb ausdrücklich als „UTF-8 ohne BOM“, in manchen Editoren heißt die Einstellung auch „UTF-8 (ohne Signatur)“.

Wenn Umlaute falsch ankommen

UTF-8 funktioniert nur, wenn Schreiber und Leser sich einig sind. Gibt eine Seite die Kopfzeile charset=UTF-8 aus, liefert aber Text in latin1, oder liest ein Script Daten ohne passende Verbindungseinstellung aus der Datenbank, entstehen die bekannten Fehlerbilder. Wie Sie am Aussehen der falschen Zeichen erkennen, an welcher Stelle es hakt, und wie Sie bereits beschädigte Einträge reparieren, beschreibt die Anleitung Umlaute falsch dargestellt Schritt für Schritt. Für das Verständnis genügt hier die Merkregel aus der Byte-Tabelle: Doppelte Zeichen mit „Ó am Anfang sind richtige UTF-8-Bytes, die falsch gelesen wurden. Eine einzelne Raute ist ein Byte, das in UTF-8 nicht vorkommen darf. Und ein Fragezeichen ist ein Zeichen, das beim Umwandeln in eine kleinere Tabelle verloren ging.

Der UTF-8-Zeichensatz im Bildergalerie-Script

Seit Version 3.15 ist UTF-8 an jeder Stelle der Kette fest eingestellt, vom Server bis zur Datenbank. Die Übersicht zeigt, wo das jeweils geschieht und ob Sie selbst etwas tun müssen.

Glied der KetteSo ist es im Script gelöstIhre Aufgabe
Kopfzeile vom Serverjede Seite sendet Content-Type: text/html; charset=UTF-8keine
Seitenkopftpl/bildergalerie.html beginnt mit <meta charset="utf-8">eigene Vorlage ebenfalls als UTF-8 speichern
Verbindung zur Datenbankimmer utf8mb4keine
Tabellenneue Installation: utf8mb4_unicode_ci, ältere Galerie: latin1nichts, Umstellung freiwillig
Script-DateienUTF-8 ohne BOM ausgeliefertbeim Bearbeiten so speichern

Tabellen: utf8mb4 oder latin1

Eine Galerie, die aus einer älteren Version aktualisiert wurde, behält ihre Tabellen im Zeichensatz latin1. Das ist kein Fehler. Weil die Verbindung mit utf8mb4 arbeitet, wandelt MySQL beim Schreiben und Lesen selbst um, und die Umlaute kommen richtig an. Auch das Eurozeichen hat dort Platz, denn MySQL-latin1 ist Windows-1252. Nur Zeichen, die darin fehlen, etwa Emojis oder ein polnisches „ń“, lassen sich nicht speichern. Welchen Stand Ihre Tabellen haben, zeigt phpMyAdmin in der Tabellenübersicht in der Spalte „Kollation“.

Dateinamen und der Schlüssel mit 190 Zeichen

Damit derselbe Dateiname in einer Kategorie nicht zweimal vorkommt, hat die Tabelle bilder einen eindeutigen Schlüssel aus Kategorienummer und Dateiname. Ein solcher Schlüssel ist ein Index, eine Art sortiertes Verzeichnis, in dem MySQL schnell nachschlägt. Und für Indizes gilt eine Grenze in Byte: Ältere oder streng eingestellte MySQL-Server erlauben pro Indexspalte höchstens 767 Byte. Weil utf8mb4 für jedes Zeichen vier Byte einplanen muss, passen höchstens 191 Zeichen hinein, denn 191 mal 4 ergibt 764.

Das Script nimmt deshalb nur die ersten 190 Zeichen des Dateinamens in den Schlüssel auf, in der Tabellendefinition steht dateiname(190). 190 mal 4 Byte ergeben 760 Byte, der Schlüssel bleibt damit auch auf strengen Servern unter der Grenze. Wichtig wird das, wenn Sie eine alte latin1-Galerie freiwillig auf utf8mb4 umstellen. Dort reichte vorher ein Byte pro Zeichen, nach der Umstellung braucht derselbe Index das Vierfache, und MySQL bricht mit Fehler 1071 „Specified key was too long“ ab. Kürzen Sie den Schlüssel dann in derselben Anweisung auf dateiname(190).

Die Dateinamen selbst enthalten ohnehin keine Umlaute. Beim Hochladen schreibt die Galerie „ü“ als „ue“ aus und setzt alles klein. Namen, die ein Windows-FTP-Programm im alten Zeichensatz schickt, wandelt sie vorher nach UTF-8 um, und zerlegte Umlaute aus macOS setzt sie wieder zu einem Zeichen zusammen.

Längengrenzen zählen Zeichen, nicht Bytes

Eine Bildbeschreibung darf höchstens 500 Zeichen lang sein, und „Größenvergleich“ zählt dabei als 15, obwohl es 17 Byte belegt. Ist ein Text länger, kürzt das Script ihn auf genau 500 Zeichen und trennt dabei kein „ö“ in der Mitte durch. Dasselbe gilt für das Passwort der Verwaltung: Es braucht mindestens 8 Zeichen, und „Bärhöhle“ erfüllt die Regel mit genau 8 Zeichen, obwohl es 10 Byte lang ist.

Beim Speichern prüft die Galerie außerdem, ob eine Beschreibung gültiges UTF-8 ist. Text im älteren Windows-Zeichensatz wandelt sie um, statt ihn zu verwerfen. Steuerzeichen und Zeilenumbrüche ersetzt sie durch Leerzeichen.

In inc/config.php finden Sie noch die Zeile $sChartSet = 'UTF-8';. Sie bleibt nur aus Kompatibilitätsgründen stehen, die Galerie gibt unabhängig von diesem Wert immer UTF-8 aus. Wer das an einer eigenen Installation nachprüfen möchte, kann das Bildergalerie-Script kostenlos herunterladen und sich nach der Installation die Kopfzeilen einer Galerieseite in den Entwicklerwerkzeugen des Browsers ansehen.

Häufige Fragen

Diese vier Fragen kommen beim Einrichten und bei der Fehlersuche am häufigsten.

Welche Meta-Angabe gehört für den UTF-8-Zeichensatz in den Seitenkopf?

<meta charset="utf-8">, und zwar so weit oben im <head> wie möglich. Der Browser muss den Zeichensatz kennen, bevor er den ersten Umlaut liest, deshalb gehört die Angabe vor den Titel. Sendet der Server zusätzlich eine Kopfzeile mit Zeichensatz, gilt diese vorrangig. Beide sollten deshalb dasselbe sagen.

Warum reicht es nicht, nur die HTML-Seite auf UTF-8 zu stellen?

Die Meta-Angabe sagt dem Browser nur, wie er lesen soll. Ob die Bytes, die er bekommt, wirklich UTF-8 sind, entscheiden andere: der Editor beim Speichern der Vorlage, die Verbindung zur Datenbank beim Auslesen der Beschreibungen. Bei mysqli legt mysqli_set_charset() den Zeichensatz der Verbindung fest, in reinem SQL tut SET NAMES utf8mb4 dasselbe.

Was ist der Unterschied zwischen utf8 und utf8mb4 in der Datenbank?

Der Zeichensatz, den MySQL lange „utf8“ nannte, speichert höchstens drei Byte pro Zeichen. Umlaute und Eurozeichen passen hinein, Emojis mit ihren vier Byte nicht. utf8mb4 ist das vollständige UTF-8, „mb4“ steht für bis zu vier Byte. Für neue Tabellen ist utf8mb4 die richtige Wahl, das alte utf8 gilt in MySQL inzwischen als veraltet.

Kann ich $sChartSet in der Galerie auf ISO-8859-1 umstellen?

Das bewirkt nichts. Die Variable steht in inc/config.php nur noch aus Kompatibilitätsgründen, ausgewertet wird sie nicht. Die Galerie sendet ihre Seiten seit Version 3.15 immer als UTF-8 und arbeitet mit einer utf8mb4-Verbindung zur Datenbank. Ältere Texte, die noch im Zeichensatz ISO-8859-1 gespeichert sind, wandelt sie bei der Ausgabe selbst um.

Verwandte Themen

Damit UTF-8 von der Datei bis zum Browser durchgehend stimmt, helfen diese Artikel beim Einrichten und bei hartnäckigen Zeichenfehlern.