Jürgen Trapp: utf8_unicode_ci oder utf8_general_ci

Hallo!

Ein paar Fragen zu UTF-8. Ich hoffe, die Informationen sind hinreichend, meine Fragen zu klären.

1. Welche Einstellung sollte ich nehmen, um Daten als UTF-8 in MySQL zu speichern? Unter "Zeichensatz / Kollation der MySQL-Verbindung:" steht utf8_unicode_ci.

2. Ist case-insensitive eigentlich sinnvoll bei Vergleichen oder käme auch utf8_bin in Frage?

3. Nach Umstellung eines Projekts auf UTF-8 kommen Eingaben zwar als UTF-8 in den Tabellen an, im PMA werden Umlaute aber nicht richtig dargestellt. Aus einem "ä" wird dann "ä", auf der Website wird es aber richtig als "ä" ausgegeben. Umgekehrt werden direkte Umlauteingaben mit PMA zwar im PMA richtig, dafür aber auf der Website falsch dargestellt. Der PMA hat die Nummer 2.11.4. Sowohl für die Verbindung wie für die Datenbank wie auch für die Tabellen und die enstprechenden Felder ist utf8_unicode_ci eingestellt, es wird per <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> UTF-8 eingestellt, eine Zeichensatzwahl durch Senden eines UTF-8-Header habe ich nicht, da es auch mit meta funktioniert (bzw. sogar ohne). Die Seiten sind mit UTF-8 ohne BOM gespeichert.

4. Sollte man <meta http-equiv="Charset" content="UTF-8" /> gleichzeitig mit
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> senden? Wenn ich header('Content-Type: text/html; charset=utf-8'); sende, kann im Prinzip auch beides entfallen!?

Besten Dank schon mal im voraus.

  1. Hallo Jürgen,

    1. Sollte man <meta http-equiv="Charset" content="UTF-8" /> gleichzeitig mit
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> senden? Wenn ich header('Content-Type: text/html; charset=utf-8'); sende, kann im Prinzip auch beides entfallen!?

    Das erste kann denke ich immer entfallen, das zweite kann ganz sinnvoll sein, wenn man sich die Dokumente offline ansieht und folglich keinen Haeder hat. Im Prinzip reiht der header aber aus (den man natürlich nicht explizit durch PHP erzeugen muss, sondern z.B. auch irgendwo in den Konfig-Dateien einstellen kann).

    Jonathan

    1. Hallo Jürgen,

      1. Sollte man <meta http-equiv="Charset" content="UTF-8" /> gleichzeitig mit
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> senden? Wenn ich header('Content-Type: text/html; charset=utf-8'); sende, kann im Prinzip auch beides entfallen!?

      das zweite kann ganz sinnvoll sein, wenn man sich die Dokumente offline ansieht

      Hallo Jonathan,

      das ist ein gutes Argument. Deine Antwort hat mir zu dieser Frage insgesamt  ausreichend Klärung gebracht, vielen Dank. Bleiben noch die weiteren Fragen. Hierzu auch eine Idee?

  2. Moin!

    1. Welche Einstellung sollte ich nehmen, um Daten als UTF-8 in MySQL zu speichern? Unter "Zeichensatz / Kollation der MySQL-Verbindung:" steht utf8_unicode_ci.

    Das hängt davon ab, welche Sortierung du haben willst. utf8_unicode_ci und utf8_general_ci unterscheiden sich.

    1. Ist case-insensitive eigentlich sinnvoll bei Vergleichen oder käme auch utf8_bin in Frage?

    Das hängt davon ab, was du für Vergleiche machst.

    1. Nach Umstellung eines Projekts auf UTF-8 kommen Eingaben zwar als UTF-8 in den Tabellen an, im PMA werden Umlaute aber nicht richtig dargestellt. Aus einem "ä" wird dann "ä", auf der Website wird es aber richtig als "ä" ausgegeben. Umgekehrt werden direkte Umlauteingaben mit PMA zwar im PMA richtig, dafür aber auf der Website falsch dargestellt. Der PMA hat die Nummer 2.11.4. Sowohl für die Verbindung wie für die Datenbank wie auch für die Tabellen und die enstprechenden Felder ist utf8_unicode_ci eingestellt, es wird per <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> UTF-8 eingestellt, eine Zeichensatzwahl durch Senden eines UTF-8-Header habe ich nicht, da es auch mit meta funktioniert (bzw. sogar ohne). Die Seiten sind mit UTF-8 ohne BOM gespeichert.

    Du hast der Datenbank - im Gegensatz zu PHPMyAdmin - nicht mitgeteilt, dass du UTF-8-codierte Strings sendest. Deshalb gilt der Standard, und das ist Latin1. Damit hast du dir deine Umlaute in der Datenbank zerstört, alles, was an UTF-8-Funktionalität wirken könnte, kann nicht mehr wirken. Du mußt alle Texte nochmal unter Latin1 aus der Datenbank auslesen und neu mit UTF-8 wieder hineinschreiben, um sie zu retten.

    Der allererste Befehl nach dem DB-Connect muß lauten: SET NAMES utf8. Erst danach dürfen Querys gemacht werden.

    1. Sollte man <meta http-equiv="Charset" content="UTF-8" /> gleichzeitig mit
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> senden? Wenn ich header('Content-Type: text/html; charset=utf-8'); sende, kann im Prinzip auch beides entfallen!?

    Da das erste <meta> nicht existiert, sondern nur das zweite, ist die Wahl hier klar. Die äquivalente Header-Angabe hat vor <meta>-Angaben allerdinsg Vorrang und verhindert, dass der Server irgendwoher eine abweichende Angabe verwendet.

    - Sven Rautenberg

    --
    "Love your nation - respect the others."
  3. echo $begrüßung;

    1. Welche Einstellung sollte ich nehmen, um Daten als UTF-8 in MySQL zu speichern? Unter "Zeichensatz / Kollation der MySQL-Verbindung:" steht utf8_unicode_ci.

    Für das Speichern ist allein die Enstellung des jeweiligen Feldes verantwortlich. Tabellen-, Datenbank- und System-Einstellungen sind nur Defaultwerte, wenn beim Anlegen keine explziten Angaben gemacht werden.

    1. Ist case-insensitive eigentlich sinnvoll bei Vergleichen oder käme auch utf8_bin in Frage?

    Diese Frage musst du dir selbst beantworten. Das kommt ganz drauf an, was du für Ergebnisse beim Suchen und Vergleichen haben möchtest.

    Die Frage aus dem Thema beantwortet dir das MySQL-Handbuch im Kapitel Unicode Character Sets.

    1. Nach Umstellung eines Projekts auf UTF-8 kommen Eingaben zwar als UTF-8 in den Tabellen an, im PMA werden Umlaute aber nicht richtig dargestellt. Aus einem "ä" wird dann "ä", auf der Website wird es aber richtig als "ä" ausgegeben. Umgekehrt werden direkte Umlauteingaben mit PMA zwar im PMA richtig, dafür aber auf der Website falsch dargestellt.

    Nein, sie werden vielleicht vom Client als UTF-8 gesendet und erwartet, jedoch ist keine Verbindungskodierung ausgehandelt worden, weswegen der Systemdefaultwert Latin1 herangezogen wird. Der PMA hat seine Hausaufgaben gemacht und sendet und erwartet UTF-8, und hat dies für sich auch so ausgehandelt. Bei deinen unausgehandelten Verbindungen interpretiert MySQL die beiden Bytes eines UTF-8-kodierten ä als einzelne Latin1-Zeichen.

    Sowohl für die Verbindung wie für die Datenbank wie auch für die Tabellen und die enstprechenden Felder ist utf8_unicode_ci eingestellt,

    Das ist alles schön und gut, auch wenn letzlich nur die Feldkodierung interessiert.

    es wird per <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> UTF-8 eingestellt, eine Zeichensatzwahl durch Senden eines UTF-8-Header habe ich nicht, da es auch mit meta funktioniert (bzw. sogar ohne). Die Seiten sind mit UTF-8 ohne BOM gespeichert.

    Das hingegen ist wichtig zwischen Webserver und Browser, aber aus der Sicht MySQLs uninteressant. Hauptsache du sendest die Kodierung, die ausgehandelt oder defaulteingestellt ist. Für Verbindungen ist das der Wert character set connection, der unter anderem über den PMA-Punkt Servervariablen und -Einstellungen abgefragt werden kann. Wenn die Antwort zwei Zeilen ist, dann ist es die für "Globaler Wert". Die andere ist die Einstellung für die aktuelle Verbindung. Die nützt dir in deinen Scripten nichts, denn du hast ja eigene Verbindungen.

    Die Verbindungskodierung setzt man mit den PHP-Funktionen mysql_set_charset() oder mysqli_set_charset(). Steht beides nicht zur Verfügung, kann man ersatzweise SET NAMES verwenden.

    1. Sollte man <meta http-equiv="Charset" content="UTF-8" /> gleichzeitig mit
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> senden? Wenn ich header('Content-Type: text/html; charset=utf-8'); sende, kann im Prinzip auch beides entfallen!?

    Die erste Meta-Element-Schreibweise ist mir noch nicht begegnet. http-equiv bedeutet HTTP-Äquivalent. Das hieße, ein solches Meta-Element überschriebe einen gleichnamigen HTTP-Header. Es ist jedoch in der für HTTP zuständigen RFC 2616 kein Header namens Charset definiert. Diese Angabe dürfte also nutzlos sein.

    Die charset-Angabe aus dem Content-Type-Meta-Element wird dann herangezogen, wenn kein HTTP-Header das charset angibt. Das ist auch dann der Fall, wenn eine Webseite als Datei abgelegt ist.

    echo "$verabschiedung $name";

    1. Hallo dedlfix,

      Deine ausführliche Antwort hilft mir, denke ich, sehr weiter. Das mit den http-equivs habe ich auch erst jetzt dank Dir richtig begriffen. Vorher habe ich es "halt so abgeschrieben", doch jetzt ist mir das System dahinter, womit man HTTP-Header überschreibt, klar geworden.

      Ich werde dann mal versuchen, Deine Antwort nachzuvollziehen bzw. umzusetzen. Vielen Dank! Ein Danke aber auch an Sven Rautenberg.

      Mal sehen, ob ich es mit Euren Antworten in den Griff kriege, aber ich denke schon. Ich melde mich dann noch mal bei Erfolg oder Mißerfolg.

      1. Nach Umstellung eines Projekts auf UTF-8 kommen Eingaben zwar als UTF-8 in den Tabellen an, im PMA werden Umlaute aber nicht richtig dargestellt. Aus einem "ä" wird dann "ä", auf der Website wird es aber richtig als "ä" ausgegeben. Umgekehrt werden direkte Umlauteingaben mit PMA zwar im PMA richtig, dafür aber auf der Website falsch dargestellt.

      Nein, sie werden vielleicht vom Client als UTF-8 gesendet und erwartet, jedoch ist keine Verbindungskodierung ausgehandelt worden, weswegen der Systemdefaultwert Latin1 herangezogen wird. Der PMA hat seine Hausaufgaben gemacht und sendet und erwartet UTF-8, und hat dies für sich auch so ausgehandelt. Bei deinen unausgehandelten Verbindungen interpretiert MySQL die beiden Bytes eines UTF-8-kodierten ä als einzelne Latin1-Zeichen.

      Hat fuktioniert mit mysql_set_charset(). Umlaute werden jetzt in der Applikation wie auch im PMA richtig dargestellt. Die zuvor vermeintlich auf der Website richtig ausgegebenen Umlaute kommen jetzt erwartungsgemäß verstümmelt an. Nur zu meinem Verständnis: Wie ist das vor mysql_set_charset() gelaufen? Die Daten kamen vom Benutzer bzw. Browser als UTF-8. In die DB wurden sie als Latin1 geschrieben, weswegen sie vom PMA zerschossen wurden, das ist mir soweit klar. Von MySQL kamen sie als Latin1 zurück an den PHP-Interpreter. Doch was ist jetzt passiert? Wieso hat die Applikation die Latin1-Daten trotz des UTF-8-Metas die Umlaute "richtig" dargestellt? Wurden die falschen "Multibyte"-Latin1-Zeichen, die im Prinzip statt einzelner Umlaute einfach mehrere Zeichen waren, vom Browser durch das UTF-8-Meta als einzelne richtige Multibyte-UTF-8-Zeichen interpretiert, oder wie muß ich mir das vorstellen?

      Noch mal zur ersten Frage:

      utf8_unicode_ci oder utf8_general_ci? Oder ist es sinnvoller, wenn ich mir versuche die Frage selbst mit http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html zu beantworten?

      1. echo $begrüßung;

        Wie ist das vor mysql_set_charset() gelaufen? Die Daten kamen vom Benutzer bzw. Browser als UTF-8. In die DB wurden sie als Latin1 geschrieben, weswegen sie vom PMA zerschossen wurden, das ist mir soweit klar. Von MySQL kamen sie als Latin1 zurück an den PHP-Interpreter. Doch was ist jetzt passiert?

        PHP hat UTF-8-kodierte Daten (vermutlich von Browser erhalten, und das auch noch korrekt kodiert) gesendet. Jedoch hat MySQL unter der Annahme, es kämen Latin1-Daten die Bytesequenz eines UTF-8-Zeichens oberhalb von 0x7F als einzelne Zeichen betrachtet. Wenn die Feldkodierung auf UTF-8 eingestellt war, hat MySQL nun diese beiden vermeintlichen Latin1-Zeichen einzeln nach UTF-8 umkodiert und abgelegt. Ein LENGTH() sollte 4 Bytes zeigen, ein CHAR_LENGTH() hingegen 2 Zeichen, zuzüglich der Zeichen unterhalb von 0x7F.

        Der PMA forderte auf seiner Verbindung UTF-8-kodierte Daten an. Beim Auslesen war also keine Umkodierung erforderlich. Deine Applikation hat nichts gesagt und damit galt Latin1. MySQL hat nun die UTF-8-kodierten Daten zurück nach Latin1 gewandelt. Bei den ehemals fehlerhaft eingelesenene Daten wurden aus den 4 UTF-8-Bytes wieder die zwei Latin1-Bytes, die du als UTF-8 interpretiert hast. Die ordentlich kodierten per PMA eingegebenen Zeichen wurden ebenfalls nach Latin1 umkodiert, wenn das betreffende Zeichen überhaupt in Latin1 verfügbar war. Ein einzelnes Latin1-Zeichen oberhalb von 0x7F ist aber keine gültige UTF-8-Bytesequenz, was dann zu dem auf der Spitze stehenden Fragezeichenquadrat führt. Ein testweises Umschalten des Browsers über das Menü Ansicht (Zeichen-)Kodierung nach ISO-8859-1 bzw. Win-1252 sollte dir diese Zeichen richtig anzeigen.

        Wieso hat die Applikation die Latin1-Daten trotz des UTF-8-Metas die Umlaute "richtig" dargestellt? Wurden die falschen "Multibyte"-Latin1-Zeichen, die im Prinzip statt einzelner Umlaute einfach mehrere Zeichen waren, vom Browser durch das UTF-8-Meta als einzelne richtige Multibyte-UTF-8-Zeichen interpretiert, oder wie muß ich mir das vorstellen?

        Du bekamst von MySQL die zwei Byte, die du irgendwann mal hingesendet hast, zurückgeliefert, und die du nun wieder als UTF-8 betrachtend einfach durchgereicht hast.

        Ohne den PMA hättest du den Fehler gar nicht bemerkt. Erst wenn du Funktionen wie LENGTH() oder CHAR_LENGTH() verwendet hättest, wäre der Fehler zum Vorschein gekommen. Auch Sortierungen dürften auf der Basis falscher Kodierungen nur eher zufällig richtig funktioniert haben.

        utf8_unicode_ci oder utf8_general_ci? Oder ist es sinnvoller, wenn ich mir versuche die Frage selbst mit http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html zu beantworten?

        Es gibt geringe Unterschiede in der Sortierung. Welche Variante dir lieber ist, musst du selbst klären. Das Handbuch zählt dir die Unterschiede auf.

        echo "$verabschiedung $name";

        1. Hallo dedlfix!

          So hatte ich mir das in etwa vorgestellt.

          Ein LENGTH() sollte 4 Bytes zeigen, ein CHAR_LENGTH() hingegen 2 Zeichen, zuzüglich der Zeichen unterhalb von 0x7F.

          Gilt diese Differenz auch noch bei neueren MySQL-Versionen, oder liefern dort beide Funktionen bei richtiger Verbindungseinstellung UTF-8 den gleichen Wert 2?

          utf8_unicode_ci oder utf8_general_ci? Oder ist es sinnvoller, wenn ich mir versuche die Frage selbst mit http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html zu beantworten?

          Es gibt geringe Unterschiede in der Sortierung. Welche Variante dir lieber ist, musst du selbst klären. Das Handbuch zählt dir die Unterschiede auf.

          Ok, dann werde ich mich dazu an dieser Stelle schlau machen.

          1. echo $begrüßung;

            Ein LENGTH() sollte 4 Bytes zeigen, ein CHAR_LENGTH() hingegen 2 Zeichen, zuzüglich der Zeichen unterhalb von 0x7F.
            Gilt diese Differenz auch noch bei neueren MySQL-Versionen, oder liefern dort beide Funktionen bei richtiger Verbindungseinstellung UTF-8 den gleichen Wert 2?

            Ja bzw. nein, LENGTH() ist definiert als Anzahl der Bytes, egal welche Kodierung vorliegt. CHAR_LENGTH() liefert die Anzahl der Zeichen, die bei Multibyte-Kodierungen von LENGTH() abweicht.

            echo "$verabschiedung $name";

            1. Hallo.

              Gilt diese Differenz auch noch bei neueren MySQL-Versionen, oder liefern dort beide Funktionen bei richtiger Verbindungseinstellung UTF-8 den gleichen Wert 2?

              Ja bzw. nein, LENGTH() ist definiert als Anzahl der Bytes, egal welche Kodierung vorliegt. CHAR_LENGTH() liefert die Anzahl der Zeichen, die bei Multibyte-Kodierungen von LENGTH() abweicht.

              Ich war mir nicht sicher, ob das früher anders war. Deine Definition ist aber auch auf der von Dir verlinkten Seite gut beschrieben. Vielen Dank nochmals!