Antworten auf Ihre häufigsten Fragen

MySQL und Zeichensätze
pdf print

Bei der Verwendung von MySQL 5 kommt es immer wieder zu Problemen mit der Darstellung von Web-Seiten in verschiedenen Zeichensätzen. Dies liegt daran, dass MySQL 5 Unicode-fähig ist. Dadurch ist es erheblich einfacher geworden, mehrsprachige Web-Seiten anzubieten, es ist jedoch auch erforderlich, auf korrekte Zeichenkodierungen zu achten.

Der richtige Zeichensatz in HTML-Dokumenten

Ein weit verbreiteter und oft ausreichender Zeichensatz für z.B. westeuropäischen Inhalt ist ISO-8859-1 (auch bekannt unter „Latin-1“). Er stammt aus der ISO-8859-Familie und beinhaltet unter Anderem die deutschen Umlaute und einige relevante Sonderzeichen. Wird das Dokument mit diesem Zeichensatz ausgeliefert, können alle darin enthaltenen Zeichen, mit Ausnahme einiger Zeichen, die in HTML eine besondere Bedeutung besitzen (z.B. <, > und &), direkt im HTML-Quelltext verwendet werden.

Da ISO-8859-1 nur ein Byte zur Kodierung eines Zeichens verwendet, können damit nur 256 Zeichen dargestellt werden. Was tut man aber, möchte man auf seiner Web-Seite Zeichen darstellen, die in dem gewählten Zeichensatz nicht enthalten sind? Eine Möglichkeit ist es, diese Zeichen mit Hilfe einer speziellen numerischen Notation einzufügen. Mit dieser Notation lässt sich jedes Zeichen aus dem Unicode-Zeichenvorrat notieren. Das große, griechische Sigma (Σ) z.B. ist in ISO-8859-1 nicht enthalten, es kann also nicht im Quelltext verwendet werden, der Browser würde ein anderes Zeichen darstellen. Um es dennoch darzustellen, könnte die angesprochene numerische Notation verwendet werden, in diesem Fall „Σ“. Diese Praktik ist jedoch, wenn überhaupt, nur für einzelne Zeichen sinnvoll.

UTF-8 für mehrsprachige Web-Seiten

Sollen ganze Sätze einer anderen Sprache dargestellt werden, bietet sich ein Zeichensatz mit einem größeren Zeichenvorrat an. UTF-8 z.B. kodiert Zeichen nicht in einem, sondern in einer Variablen Länge von Bytes - dadurch lassen sich alle Zeichen des Unicode-Systems darstellen. Sie können also direkt in den HTML-Quelltext notiert werden, sofern der verwendete Editor selbst UTF-8 unterstützt.

Mit welchem Zeichensatz die empfangene Ressource zu dekodieren ist, erfährt der anfordende Browser vom Webserver. Dazu muss der Webserver erfahren, dass er diese Angabe mitsenden soll, was auf verschiedene Arten geschehen kann:

  • in PHP steht dazu die Funktion header() zur Verfügung. Mittels header('Content-Type: text/html; charset=utf-8'); wird dem Webserver der verwendete MIME-Typ und Zeichensatz mitgeteilt.
  • in einer .htaccess-Datei lässt sich dem Webserver mittels AddCharset mitteilen, bestimmte Ressourcen mit einem definierten Zeichensatz auszuliefern. So veranlasst die Zeile AddCharset utf-8 .html .php den Webserver, sämtliche Dokumente mit der Datei-Endung html und php als Ressourcen mit dem Zeichensatz UTF-8 auszuliefern. Möchte man sämtlichen Antworten, die mit dem MIME-Typ text/plain und text/html ausgeliefert werden, außerdem den Zeichensatz UTF-8 vorgeben, genügt eine Zeile, die die Anweisung AddDefaultCharset enthält: AddDefaultCharset utf-8

Damit auch nach dem Abspeichern des Dokuments noch der korrekte Zeichensatz verwendet wird, gibt es außerdem die im HTML-Quelltext zu notierende Meta-Angabe zum Zeichensatz http-equiv="content-type". Diese sollte zwar auch vom Webserver interpretiert werden, allerdings nur, wenn kein HTTP-Header angegeben ist - es würde keinen Sinn ergeben, eine hiervon abweichende Angabe zu notieren. Im Konfliktfall sollte vom Browser die Angabe im HTTP-Header den Vorzug erhalten, was dann zu der Merkwürdigkeit führen könnte, dass das gleiche Dokument, über den Webserver ausgeliefert, im Unterschied zu einem lokalen Aufruf anders dekodiert würde, denn lokal aufgerufen steht dem Browser natürlich kein HTTP-Header zur Verfügung und er würde sich wohl an die Angabe im Meta-Tag halten. Die Information, ob und welche Zeichensatz-Angabe im HTTP-Header enthalten ist, kann auf mehrere Arten festgestellt werden: viele Browser zeigen die vom Server mitgelieferte Kodierungsangabe in ihren Seiteninformationen. Außerdem gibt es sogenannte HTTP-Trace-Services wie den Web-Sniffer, welche die Header-Informationen anzeigen.

Zeichensätze und Datenbankverbindungen

Die Daten werden von MySQL intern standardmäßig in UTF-8 kodiert abgelegt, die Daten zwischen Server und Client aber in Latin-1 ausgetauscht. Soll die Webseite in UTF-8 sein, muss man dem Datenbankserver mitteilen, dass die Daten als UTF-8-kodiert ankommen und auch ausgeliefert werden sollen. Das geschieht mit der SQL-Anweisung SET NAMES 'utf8' oder SET CHARACTER SET utf8.

Die gleichen Angaben müssen auch beim Im-/Export von Daten, z.B. über den phpMyAdmin, angegeben werden. Bei DB-Dumps, die von einem MySQL-Server erstellt wurden, wird der Zeichensatz der Daten (bei deutschsprachigen Daten i.d.R. Latin-1) unter „Zeichencodierung der Datei“ ausgewählt.

„MÃŒller“ statt „Müller“

Trotzdem kommt es vor, dass auf MySQL aufbauende Web-Anwendungen die Umlaute verhunzen. Das ist in der Regel eine Folge von nicht korrekt kodiert übertragenen bzw. gespeicherten Daten. Haben wir beispielsweise ein HTML-Formular, muss dem Browser mitgeteilt werden, welchen Zeichensatz er für dieses Formular akzeptieren darf. Soll die Web-Seite als ISO-8859-1 kodiert ausgeliefert werden, der Browser sendet aber fälschlicherweise UTF-8, kommt es zu oben genannter Abweichung: der Browser sendet die Byte-Sequenz, die in ISO-8859-1 „MÃŒller“ entspricht. Gelangt diese Bytesequenz ohne Überprüfung in die Datenbank, setzt sich der Fehler fort. Um das zu verhindern, gibt es mehrere Möglichkeiten:

  • der Browser darf nur ISO-8859-1 akzeptieren: im einleitenden Formular-Element benutzt man dazu das Attribut accept-charset mit dem Wert ISO-8859-1, also z.B. <form action="speichern.php" method="post" accept-charset="ISO-8859-1">. Dieses Vorgehen hat den Nachteil, dass einige Browser Fragezeichen für eingegebene Zeichen außerhalb von ISO-8859-1 senden. Der Speichervorgang in einem verarbeitenden PHP-Script könnte beispielsweise mit folgenden Zeilen geschehen:

    <?php
    $link = mysql_connect('host', 'user', 'pass')
        or die ('Verbindung zur DB nicht m&ouml;glich');
    mysql_query('SET NAMES \'latin1\'', $link);
    mysql_query('INSERT INTO datenbank.tabelle (spalte) ' .
        'VALUES (\'' . mysql_real_escape_string($daten, $link) . '\')', $link)
        or die('INSERT fehlgeschlagen: ' . mysql_error($link));
    ?>


  • man akzeptiert und speichert generell UTF-8, indem im Formular-Element accept-charset="UTF-8" und unter MySQL die Anweisung SET NAMES 'utf8' genutzt werden. Aber auch hier können bei der Auslieferung mittels Latin-1 Zeichen verloren gehen, da Latin-1 einen kleineren Zeichenvorrat als UTF-8 besitzt.

Die Sortierung (collation) der Daten

Die Standardeinstellung für die Sortiermethode auf unseren Servern entspricht „latin1_german2_ci“. Dies liefert die für die meisten Kunden erwartete Sortiermethode. Dies gilt bei der Verwendung von UTF-8 nicht, hier gibt es eigene Sortiervorschriften. Bei der Verwendung der standardmäßigen „utf8_general_ci“ wird aber augenscheinlich „falsch“ sortiert, Umlaute und das ß wandern nach hinten, da hier nach den Byte-Werten sortiert wird und die genannten Zeichen hinter dem z eingeordnet sind. Leider bietet MySQL von Haus aus noch keine deutsche Sortiervorschrift für UTF-8-Daten. Man kann sich aber mit einem Trick behelfen, indem die Daten für den Sortiervorgang in den gewünschten Zeichensatz mit der gewünschten Sortiervorschrift konvertiert werden:

SELECT utf8_spalte FROM tabelle
ORDER BY CONVERT(utf8_spalte USING latin1) COLLATE latin1_german2_ci ASC;

Für die Sortierung werden hier die UTF-8-Werte in den Zeichensatz Latin-1 mit der Sortiervorschrift latin1_german2_ci konvertiert, die dafür sorgt, dass ä, ö, ü und ß bei der Sortierung wie ae, oe, ue und ss behandelt werden.

Beispiele

Eine Seite mit ausschließlich chinesischem Inhalt (z.B. Zeichensatz GB2312) könnte folgendermaßen aufgebaut sein:

<?php header('Content-Type: text/html; charset=GB2312'); ?>
<html>
  <head>
    <title>GB2312-Example</title>
    <meta http-equiv="Content-Type" content="text/html; charset=GB2312" />
    <meta http-equiv="Content-Language" content="zh" />
    <meta name="DC.language" scheme="DCTERMS.RFC3066" content="zh" />
    <!-- weitere Angaben im Kopf... -->
  </head>
  <body>
    <!-- beliebiger Inhalt, die chinesischen Schriftzeichen k&ouml;nnen direkt im Quelltext notiert werden -->
<?php
 /**
  * jetzt noch irgendein dynamischer Inhalt
  */
 $link = mysql_connect('host', 'user', 'pass') or die('No Connection');
 // wir wollen mit dem MySQL-Server nur noch gb2312 sprechen
 mysql_query('SET NAMES \'gb2312\'', $link);
 $result = mysql_query('SELECT gb2312_spalte FROM datenbank.tabelle...', $link); // Ergebnisse ausgeben
?>

Weitere ausführliche Erläuterungen zu Zeichensätzen und Kollationen bei MySQL finden Sie in diesem Artikel.


otto.friedrich@hosteurope.de xanthippe.ypsilante@hosteurope.de hercules.ikarus@hosteurope.de