UTF-Speicherung in MySQL
Michael
- datenbank
Hallo, ich habe ein Problem, mit der Speicherung von UTF8-kodierten Texten in MySQL.
EIn PHP Skript das im Kopf über <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> als UTF-8 angegeben und auch richtig kodiert ist. Die Datei hat mehrere Eingabefelder, die per PHP in die Datenbank geschrieben werden.
Die Details zur verwendeten Datenbank (MySQL 5.0.41):
Kollation der Datenbank -> utf8_general_ci
Kollation der Tabelle -> utf8_general_ci
Kollation der Felder -> utf8_general_ci
Wenn ich die in der Datenbank gespeicherten Texte in einer ebenfalls UTF-8 kodierten Seite ausgebe wird auch alles korrekt dargestellt. Und zwar unabhängig davon, ob ich im Browser die Textkodierung auf automatisch stelle, oder UTF-8 erzwinge.
Das einzige was mich verwundert ist, dass die Zeichen in der Datenbank selber komisch dargestellt werden. So wird z.B. ein »ö« als »Ã¶« dargestellt. Und zwar in phpMyAdmin und über den MySQL Query Browser.
Ist das denn nun korrekt und lediglich ein Darstellungsproblem? Oder habe ich mir auf dem Weg in die Datenbank irgendwo die Kodierung zerschossen und das was dort gespeichert wird ist gar kein UTF-8?
Ich bin Dankbar für jeden Hinweis.
Viele Grüße, Michael
Hi Michael,
Das einzige was mich verwundert ist, dass die Zeichen in der Datenbank selber komisch dargestellt werden. So wird z.B. ein »ö« als »Ã¶« dargestellt. [...] Ist das denn nun korrekt und lediglich ein Darstellungsproblem? Oder habe ich mir auf dem Weg in die Datenbank irgendwo die Kodierung zerschossen und das was dort gespeichert wird ist gar kein UTF-8?
Das ist eigentlich nicht korrekt, aber solange du die Daten mit dem gleichen Fehler in die Datenbank schreibst, wie du sie auslest, funktioniert es (wie du gemerkt hast) trotzdem ;-)
Korrekt wäre, für die Verbindung zur Datenbank noch mal UTF-8 zu wählen, was mit SET NAMES UTF8
geschehen sollte. Siehe auch im MySQL Manual unter Verbindungszeichensatz.
Viele Grüße,
~ Dennis.
echo $begrüßung;
Oder habe ich mir auf dem Weg in die Datenbank irgendwo die Kodierung zerschossen und das was dort gespeichert wird ist gar kein UTF-8?
MySQL interpretiert die Daten in dem Fall als Latin1. Also jedes Byte einer UTF-8-Sequenz als eigenständiges Zeichen.
Das ist eigentlich nicht korrekt, aber solange du die Daten mit dem gleichen Fehler in die Datenbank schreibst, wie du sie auslest, funktioniert es (wie du gemerkt hast) trotzdem ;-)
Es hört dann auf zu funktionieren, wenn man Stringoperationen (Sortieren, Vergleichen, Teilstrings bilden) in MySQL machen möchte.
Korrekt wäre, für die Verbindung zur Datenbank noch mal UTF-8 zu wählen, was mit
SET NAMES UTF8
geschehen sollte. Siehe auch im MySQL Manual unter Verbindungszeichensatz.
Eigentlich ist es besser, die dafür vorgesehene Funktion der MySQL-Client-API zu verwenden: mysql_set_character_set(). Somit erfährt auch die Client-API und nicht nur der Server von der zu verwendenen Kodierung und lässt mysql_real_escape_string() korrekt arbeiten. Dies ist allerdings für ISO-8859-X/LatinX und UTF-8 nicht so wichtig. Für diese Kodierungen reicht auch SET NAMES.
PHP kennt ein Pendant zu dieser Funktion für die mysqli-Extension schon lange (mysqli_set_charset()), für die mysql-Extension erst seit 5.2.3 (mysql_set_charset())
echo "$verabschiedung $name";
Hi dedlfix,
PHP kennt ein Pendant zu dieser Funktion für die mysqli-Extension schon lange (mysqli_set_charset()), für die mysql-Extension erst seit 5.2.3 (mysql_set_charset())
Und für Pdo_MySQL anscheinend gar nicht… hört sich zumindest nach diesem Kommentar so an:
pdo doesn't care about charsets. if you want to have your connection in unicode / utf-8
or any other encoding, you'll have to tell your database, for example using
$dbh->exec('SET CHARACTER SET utf8') (mysql).
Da frage ich mich doch gerade, ob ich nicht statt PDO vielleicht lieber wieder mysqli verwenden sollte, wenn in dem meisten Fällen sowieso nur eine MySQL-Datenbank dahinter zum Einsatz kommt…
Viele Grüße,
~ Dennis.
echo $begrüßung;
PHP kennt ein Pendant zu dieser Funktion für die mysqli-Extension schon lange (mysqli_set_charset()), für die mysql-Extension erst seit 5.2.3 (mysql_set_charset())
Und für Pdo_MySQL anscheinend gar nicht… hört sich zumindest nach diesem Kommentar so an:
pdo doesn't care about charsets. if you want to have your connection in unicode / utf-8
or any other encoding, you'll have to tell your database, for example using
$dbh->exec('SET CHARACTER SET utf8') (mysql).
SET CHARACTER SET arbeitet etwas anders als SET NAMES, wenn unterschiedliche Kodierungen für Verbindung und gespeicherten Daten verwendet werden. Im Allgemeinen möchte man aber nur eine einzige Kodierung im gesamten Prozess haben und sollte dann auch SET NAMES verwenden.
Da frage ich mich doch gerade, ob ich nicht statt PDO vielleicht lieber wieder mysqli verwenden sollte, wenn in dem meisten Fällen sowieso nur eine MySQL-Datenbank dahinter zum Einsatz kommt…
Auf ein anderes DBMS nur durch Ändern des Connection-String umsteigen zu können, ist meist nur eine Illusion. Man muss sich dann auch bei den verwendeten Statements und Datentypen auf das kleinste gemeinsame Vielfache aller DBMS einschränken, wobei einem jede Menge Leistungsmerkmale des gerade verwendeten DBMS verschlossen bleiben. Insofern halte ich den Einsatz einer Abstraktionsschicht aus diesem Grund für nicht weit genug gedacht.
In der Praxis passiert es gelegentlich, dass man für verschiedene Projekte unterschiedliche DBMS zu verwenden hat, und da ist es schon nicht schlecht, wenn man die einmal gelernte API-Bedienung weiterverwenden kann. Es bleiben noch genug Unterschiede in der SQL-Syntax zum lernen übrig. Außerdem bilden manche APIs auch noch Funktionalität nach, die es nativ nicht gibt. Ein Beispiel sind Prepared Statements, die es für die mysql-Extension PHPs nicht gibt.
PDO mit mysqli verglichen, fällt mir das umständlichere Prepared-Statements-Handling unter mysqli ein. Das ist unter PDO bedienerfreundlicher gelöst (auch wenn es diese Funktionalität intern nachbildet, obwohl es auf die MySQL-Client-API zugreifen könnte). Weiterhin gibt es eine Menge netter Fetch-Möglichkeiten unter PDO, beispielsweise eine komplette Spalte auf einmal fetchen können, ohne dafür eine Schleife verwenden zu müssen.
Das Problem mit den Charset - nun, zumindest für Latin/ISO-8859 und UTF-8 kann man es mit SET NAMES lösen. Und wenn man wirklich portabel programmieren will, kommt man um eine Fallunterscheidung beim Einstellen des Charsets, so es sich mit einem Statement erledigen lässt, für die einzelnen drunterliegenden Systeme wohl nicht umhin.
echo "$verabschiedung $name";
Danke für die Infos.
Korrekt wäre, für die Verbindung zur Datenbank noch mal UTF-8 zu wählen, was mit
SET NAMES UTF8
geschehen sollte. Siehe auch im MySQL Manual unter Verbindungszeichensatz.
Mit SET NAMES UTF8
wird es nun korrekt in der Datenbank gespeichert. Aber der Browser gibt mir (um bei dem Beispiel zu bleiben) anstatt eines »ö« leider »�« aus.
Dass heißt ja nun, das doch mit der Kodierung meines Dokumentes etwas nicht stimmt. Daraufhin habe ich das Dokument zeitweise auf ISO Latin 1 (ISO-8859-1) umgestellt. Dann werden die Inhalte aus der datenbank korrekt dargestellt, die statischen Inhalte der Seite, jedoch nicht, weil die Umlaute hart-kodiert (ohne HTML-Entities) in dem Dokument stehen.
Ich verstehe das nicht. Was läuft hier falsch?
echo $begrüßung;
Mit SET NAMES UTF8 wird es nun korrekt in der Datenbank gespeichert.
Das heißt also, der phpMyAdmin zeigt die Umlaute der neu hinzugefügten Daten richtig an. Die Umlaute der bereits gespeicherten Daten müssten erst korrigiert werden, bevor du neue Daten einspielst. Sie reparieren sich nicht automatisch, nur weil du jetzt SET NAMES verwendest.
Aber der Browser gibt mir (um bei dem Beispiel zu bleiben) anstatt eines »ö« leider »�« aus.
Führst du SET NAMES nach jedem Verbindungsaufbau durch?
Dass heißt ja nun, das doch mit der Kodierung meines Dokumentes etwas nicht stimmt. Daraufhin habe ich das Dokument zeitweise auf ISO Latin 1 (ISO-8859-1) umgestellt. Dann werden die Inhalte aus der datenbank korrekt dargestellt, die statischen Inhalte der Seite, jedoch nicht, weil die Umlaute hart-kodiert (ohne HTML-Entities) in dem Dokument stehen.
Deine Seite scheint korrekt UTF-8-kodiert zu sein. Die eingefügten Daten aus der Datenbank sind aber gemäß Latin1/ISO-8859-1 kodiert. Du wirst sie an irgendeiner Stelle in dieser Kodierung geliefert bekommen.
Für die Kodierung der Feldinhalte ist allein die eingestellte Kodierung des jeweiligen Feldes interessant. Tabellen- und Datenbank-Werte sind nur Defaultangaben für neue Felder und Tabellen. Für die Datenübertragung von und zum Client gibt es die drei mit SET NAMES einstellbaren Konfigurationswerte. Ist Feld- und Verbindungskodierung unterschiedlich, nimmt MySQL beim Schreiben oder Lesen eine Umkodierung vor, soweit das technisch möglich ist (in der Zielkodierung das Zeichen dargestellt werden kann).
Ich verstehe das nicht. Was läuft hier falsch?
Ich vermute, du hast im auslesenden Script SET NAMES vergessen, und MySQL hat als Default-Wert Latin1 eingestellt.
echo "$verabschiedung $name";
Ich vermute, du hast im auslesenden Script SET NAMES vergessen, und MySQL hat als Default-Wert Latin1 eingestellt.
Richtig vermutet. Tausend Dank für die tolle Unterstützung. Es funktioniert nun so, wie es sollte.
Ich finde es allerdings etwas nervig, dass ich vor jedem SQL-Statement eine weitere SQL-Anfrage mit »SET NAMES UTF8« an den Server schicken muss. Wäre das denn auch notwendig, wenn die Verbindung einmal mit »mysql_pconnect()« eine persistente Verbindung zu dem Server aufbauen würde?
echo $begrüßung;
Ich finde es allerdings etwas nervig, dass ich vor jedem SQL-Statement eine weitere SQL-Anfrage mit »SET NAMES UTF8« an den Server schicken muss.
Für immer wiederkehrende Handlungen hat man beispielsweise Funktionen erfunden. Wenn du die Kontrolle über den MySQL-Server hast, kannst du dem auch die Default-Kodierung umstellen, oder über init_connect ein Statement konfigurieren, das bei jedem Connect eines Clients ausgeführt wird.
Wäre das denn auch notwendig, wenn die Verbindung einmal mit »mysql_pconnect()« eine persistente Verbindung zu dem Server aufbauen würde?
Persistente Verbindungen sind selten das was man will. Bitte Suche nach
author:"Sven Rautenberg" mysql_pconnect
im hiesigen Archiv für weitere Erklärungen.
echo "$verabschiedung $name";
Dankeschön. Damit wäre für mich alles geklärt.
Beste Grüße,
Michael
Moin!
Ich finde es allerdings etwas nervig, dass ich vor jedem SQL-Statement eine weitere SQL-Anfrage mit »SET NAMES UTF8« an den Server schicken muss. Wäre das denn auch notwendig, wenn die Verbindung einmal mit »mysql_pconnect()« eine persistente Verbindung zu dem Server aufbauen würde?
Einmal direkt nach mysql_connect() reicht vollkommen aus - und den Befehl hat man ja sowieso irgendwo in einer zentralen Funktion oder Klasse, so dass man problemlos diesen Query hinzufügen kann.
- Sven Rautenberg