MichaelR: Codierung (Charset UTF-8)

Hallo,

momentan stelle ich ein Projekt hinsichtlich der Charset-Behandlung um, d.h. bislang waren die Webseiten als ISO-8859-1 deklariert, alle Umlaute etc. kodiert. Die Datenbank (MySQL) speicherte das ganze als ISO-8859-1 entsprechend ab. Daten aus Formularen wurden auf Serverseite mittels PHP bearbeitet, so dass z. B. Umlaute kodiert wurden (ä --> ä etc.).

Jetzt hab ich auf UTF-8 umgestellt und wollte nur mal wissen, ob ich evtl. was vergessen hab.
Ich hab jetzt:

-- Webseiten haben charset=UTF-8
-- Die Datenbank (MySQL) speichert als UTF-8
-- Formulareingaben werden nicht speziell kodiert, also das was per POST ankommt bleibt soweit unbearbeitet (in Bezug auf das Charset)

Sollte ich noch auf was achten?

Grüße,
Michael

  1. Hallo,

    Hallo,

    Sollte ich noch auf was achten?

    SET NAMES gesetzt?

    Gruß, Volker

  2. Привіт!

    -- Webseiten haben charset=UTF-8
    -- Die Datenbank (MySQL) speichert als UTF-8
    -- Formulareingaben werden nicht speziell kodiert, also das was per POST ankommt bleibt soweit unbearbeitet (in Bezug auf das Charset)

    Sollte ich noch auf was achten?

    Ja, der HTTP-Header muß stimmen (also entweder UTF-8 angeben oder gar nichts), und zusätzlich ist noch eine Angabe als Meta-Tag sinnvoll (für den Fall, daß das Dokument aus einem anderen Kontext heraus geöffnet wird als aus HTTP).

    Viele Grüße vom Længlich

  3. -- Die Datenbank (MySQL) speichert als UTF-8

    Sollte ich noch auf was achten?

    Hast du die Datenbankfelder einfach nur auf UTF-8 umgestellt oder hast du zusätzlich auch die Einträge der Datenbank als UTF-8 gespeichert? Die Einträge werden nämlich nicht automatisch umgewandelt!

    Beliebte Fehlerquelle sind auch generell viele String-Funktionen. Zum Beispiel ist substr binärsicher, was dann aber selbstverständlich zur Folge hat, dass Mutlibytetexte Probleme bereiten. Wenn möglich (und vorhanden) in solchen Fällen zu den Multibyte-String-Funktionen wechseln.

    1. echo $begrüßung;

      Hast du die Datenbankfelder einfach nur auf UTF-8 umgestellt oder hast du zusätzlich auch die Einträge der Datenbank als UTF-8 gespeichert? Die Einträge werden nämlich nicht automatisch umgewandelt!

      Diese Aussage kann ich nicht bestätigen, auch wenn du sie mit einem Ausrufezeichen beendet hast. Wenn die Charset-/Kollationsangabe des Feldes beispielsweise auf Latin1 steht, und die Daten darin ordnungsgemäß Latin1-kodiert sind, dann reicht ein Umstellen auf eine andere Kodierung aus. MySQL nimmt dabei eine Umkodierung des Inhalts vor. Natürlich muss die Zielkodierung alle bisherigen Zeichen kodieren können, was bei UTF-8 kein Problem ist.

      echo "$verabschiedung $name";

      1. echo $begrüßung;

        Hast du die Datenbankfelder einfach nur auf UTF-8 umgestellt oder hast du zusätzlich auch die Einträge der Datenbank als UTF-8 gespeichert? Die Einträge werden nämlich nicht automatisch umgewandelt!

        Diese Aussage kann ich nicht bestätigen, auch wenn du sie mit einem Ausrufezeichen beendet hast. Wenn die Charset-/Kollationsangabe des Feldes beispielsweise auf Latin1 steht, und die Daten darin ordnungsgemäß Latin1-kodiert sind, dann reicht ein Umstellen auf eine andere Kodierung aus. MySQL nimmt dabei eine Umkodierung des Inhalts vor. Natürlich muss die Zielkodierung alle bisherigen Zeichen kodieren können, was bei UTF-8 kein Problem ist.

        echo "$verabschiedung $name";

        Hhmpf....meine Erfahrung sieht da etwas anders aus. Hatte damit bislang immer Probleme. Egal um welche MySQL-Version es sich gehandelt hat.
        Habs jetzt gerade auch nochmal sicherheitshalber getestet. Neue Tabelle mit einem Varchar-Feld - latin1_general_ci codiert. Eintrag mit Sonderzeichen ä gemacht. Umgestellt auf utf8_general_ci und es ist noch immer das ä zu sehen und nicht wie erwartet ä. Wirds evtl. nur per Konsole korrekt umgewandelt?

        1. echo $begrüßung;

          Bitte zitiere sinnvoll, nicht einfach alles.

          Neue Tabelle mit einem Varchar-Feld - latin1_general_ci codiert. Eintrag mit Sonderzeichen ä gemacht. Umgestellt auf utf8_general_ci und es ist noch immer das ä zu sehen und nicht wie erwartet ä.

          Womit hast du das gemacht? Mit dem phpMyAdmin? Dann war alles in bester Ordnung. Wenn du ä sähest, dann wäre was verkehrt. Du bekommst mit dem PMA die Bytes, die MySQL intern zur Speicherung verwendet, nicht zu sehen. Als Client interessiert dich erst einmal nur die Kodierung, die auf deiner Verbindung verwendet wird. MySQL nimmt die Daten in dieser Kodierung entgegen, wandelt sie gegebenenfalls um, um sie mit der für die Felder angegebenen Kodierung dort zu speichern. Beim Auslesen wandelt es ebenfalls um, in die Kodierung, die der auslesende Client ausgehandelt hat. (Ich hab das mal vereinfacht dargestellt. Das genaue Prozedere ist im Kapitel Connection Character Sets and Collations zu finden.)

          Der PMA spricht mit dem MySQL-Server UTF-8. Er sendet UTF-8, und interpretiert das was von MySQL kommt ebenfalls als UTF-8. Er schickt das als UTF-8 deklariert weiter zum Browser, der interpretiert die Bytefolge als UTF-8-kodiertes ä und malt ein solches auf den Schirm. Als das Feld noch Latin1-kodiert war, hat MySQL beim Auslesen das Latin1-kodierte ä in UTF-8 umkodiert und es dem PMA gesendet. Beim Umstellen der Feldkodierung auf UTF-8 hat MySQL den Inhalt nach UTF-8 umkodiert, und beim Auslesen reicht er es nun einfach ohne Umwandlung an den PMA durch.
          Du siehst erst dann wieder ein ä, wenn du die UTF-8-Bytefolge als Latin1/ISO-8859-1/Win-1252 zu interpretieren versuchst.

          echo "$verabschiedung $name";

          1. echo $begrüßung;

            Ich hab da nochmal was vorbereitet. Teil 1 des Versuchs schreibt ein ä in ein Latin1-Feld und fragt dieses unter den Verbindungskodierungen Latin1 und UTF-8 ab. Dann wird die Feldkodierung auf UTF-8 umgestellt und wieder mit beiden Verbindungskodierungen abgefragt. Dieser Versuch zeigt aber nur, dass MySQL auf der Verbindung die jeweils richtige Bytefolge schickt. Das Feld stellt eine Black-Box dar. Was darin passiert ist in diesem Teil nicht offensichtlich. Es kann nur vermutet werden, dass MySQL zwischen Feldkodierung und Verbindungskodierung bei Bedarf umkodiert.
            Teil 2 versucht den Deckel der Black-Box zu lüften. Er schreibt ein ä und das Nicht-Latin1-Zeichen Ω (Ohm-Zeichen; Unicode-2126) in die Tabelle und stellt dann die Feldkodierung auf Latin1 um. Das Ω geht dabei flöten, weil es nicht in Latin1 kodiert werden kann. Das ä lässt sich weiterhin unter beiden Verbindungskodierungen abfragen.

            (Der Code ist kein Beispiel für gutes mysqli-Handling unter PHP, denn ich hab sämtliche Fehlerprüfungen weggelassen. Bitte vor Ausführung sicherstellen, dass keine Tabelle names "test" existiert.)

            <pre>  
            <?php  
              
            class test_mysqli extends mysqli {  
              // Zusammenfassung von Query, Fetch und Ausgabe als Hex-Werte  
              function abfrage() {  
                $result = $this->query('SELECT feld FROM test');  
                $row = $result->fetch_assoc();  
                print bin2hex($row['feld']) . "\n";  
              }  
            }  
              
            // Vorbereitung; Anlegen der Tabelle mit einem Latin1-Feld  
            $mysqli = new test_mysqli('localhost', 'test', 'test', 'test');  
            $mysqli->set_charset('latin1'); // entspricht SET NAMES  
            $mysqli->query('CREATE TABLE test (feld VARCHAR(10) CHARACTER SET latin1)');  
            $mysqli->query("INSERT INTO test SET feld='\xE4'"); // ein ä  
              
            // Abfragen; Verbindungskodierung ist immer noch latin1  
            $mysqli->abfrage(); // Ausgabe: e4  
              
            // Abfragen; Verbindungskodierung auf utf8 umgestellt  
            $mysqli->set_charset('utf8');  
            $mysqli->abfrage(); // Ausgabe: c3a4  
              
            // Ändern der Feldkodierung auf utf8  
            $mysqli->query('ALTER TABLE test CHANGE feld feld VARCHAR(10) CHARACTER SET utf8');  
              
            // Abfragen mit Verbindungskodierung latin1  
            $mysqli->set_charset('latin1');  
            $mysqli->abfrage(); // Ausgabe: e4  
              
            // Abfragen mit Verbindungskodierung utf8  
            $mysqli->set_charset('utf8');  
            $mysqli->abfrage(); // Ausgabe: c3a4  
              
            // Gegenversuch mit Zeichen, das nicht in Latin1 enthalten ist  
            print "\n";  
            $mysqli->query('TRUNCATE test');  
            $mysqli->query("INSERT INTO test SET feld='\xc3\xa4\xe2\x84\xa6'"); // ä + Ω (Ohm-Zeichen; Unicode-2126)  
            $mysqli->abfrage('SELECT feld FROM test'); // Ausgabe: c3a4e284a6  
              
            // Abfragen mit Verbindungskodierung latin1  
            $mysqli->set_charset('latin1');  
            $mysqli->abfrage(); // Ausgabe: e43f - ein ä und ein Fragezeichen  
              
            // nochmal Abfragen mit Verbindungskodierung utf8  
            $mysqli->set_charset('utf8');  
            $mysqli->abfrage(); // Ausgabe: c3a4e284a6 - ist also noch vorhanden  
              
            // Ändern der Feldkodierung auf latin1  
            $mysqli->query('ALTER TABLE test CHANGE feld feld VARCHAR(10) CHARACTER SET latin1');  
              
            // Abfragen; Verbindungskodierung ist immer noch utf8  
            $mysqli->abfrage(); // Ausgabe: c3a43f - ä lebt noch, Ω ist tot  
              
            // Abfragen mit Verbindungskodierung latin1  
            $mysqli->set_charset('latin1');  
            $mysqli->abfrage(); // Ausgabe: e43f - ein ä und ein Fragezeichen  
              
              
            // Aufräumen  
            $mysqli->query('DROP TABLE test');
            

            echo "$verabschiedung $name";

  4. echo $begrüßung;

    -- Die Datenbank (MySQL) speichert als UTF-8

    Diese Aussage ist zu allgemein, denn es gibt nach meiner Zählung insgesamt 10 verschiedenartige Stellen, an denen eine Kodierungsangabe gemacht werden kann. Kennst du sie alle? Wichtig sind vor allem die Angaben der Feldkodierung und die der Client-Verbindung (SET NAMES wurde ja schon erwähnt).

    echo "$verabschiedung $name";

    1. Hallo,

      -- Die Datenbank (MySQL) speichert als UTF-8

      Diese Aussage ist zu allgemein, denn es gibt nach meiner Zählung insgesamt 10 verschiedenartige Stellen, an denen eine Kodierungsangabe gemacht werden kann. Kennst du sie alle? Wichtig sind vor allem die Angaben der Feldkodierung und die der Client-Verbindung (SET NAMES wurde ja schon erwähnt).

      Beim Erstellen der Tabellen habe ich

      CREATE .. () CHARACTER SET utf8

      verwendet.

      Grüße,
      Michael

      1. echo $begrüßung;

        Beim Erstellen der Tabellen habe ich
        CREATE .. () CHARACTER SET utf8

        Das ist die Default-Kodierung für die Tabelle. Sie wird nicht weiter verwendet außer zur Zuweisung dieses Wertes an neu angelegte Felder, falls denen keine Kodierung mitgegeben wurde. Da du vermutlich für die Felder nichts weiter festgelegt hast, bekommen sie also diese Kodierung zugewiesen. Wenn du nun Daten darin ablegst, wird MySQL das in dieser Kodierung tun. Gegebenenfalls nimmt es dazu Umkodierungen vor, wenn auf der Verbindung zum Client eine andere Kodierung vereinbart oder als Defaultwert angenommen wird.

        echo "$verabschiedung $name";

  5. Hallo,

    so, heute ist mir aufgefallen, dass ich doch ein Problem mit den Zeichen und insbesondere den Umlauten habe.

    Infos:
    -- MySQL-Tabellen haben Character set UTF-8
    -- Webseite ist mit Meta-Tag als UTF-8 gekennzeichnet
    -- Daten werden ohne Bearbeitung/Umwandlung so wie sie von der Webseite kommen in die Tabellen gespeichert, über PHPMyAdmin werden Umlaute etc. richtig angezeigt (Kodierung im Browser UTF-8, ebenso PHPMyAdmin)
    -- Daten aus der Datenbank werden in der Webseite auch unverändert angezeigt (also ohne die Kodierung zu verändern meine ich), also bisher stand immer ein ä, ü etc. direkt so im Quelltext (das war allerdings noch mit Kodierung iso-8859-1)

    Mein Problem:
    Die Webseiten tragen jetzt utf-8 als meta-tag Kodierung und plötzlich sind alle Umlaute falsch dargestellt, in Firefox erscheint jetzt dieses Fragezeichen-Symbol.

    Zweites Problem:
    Wenn ich in ein Formular z. B. ä eingebe und an den Server zum Speichern in der Tabelle schicke, dann steht in der Tabelle (über PHPMyAdmin mit Kodierung UTF-8) kein lesbares ä mehr drinnen, sondern "Müll", also unleserliches Zeichen anstelle des ä.

    Jemand einen Tipp wo ich ansetze um die Kodierung wieder passend zu machen, ohne dass ich alle PHP-Codeseiten um spezielle Codezeilen ergänze, die sich "manuell" um die Kodierung und die Anzeige kümmern?

    Grüße+Danke,
    Michael

    1. Hallo nochmals,

      Problem scheint gelöst zu sein, die Verbindung von Client und MySQL-Server war auf Latin1 kodiert. Hab das jetzt mittels SET NAMES und SET CHARACTER SET umgestellt auf utf8.

      Grüße,
      michael

      1. echo $begrüßung;

        Problem scheint gelöst zu sein, die Verbindung von Client und MySQL-Server war auf Latin1 kodiert. Hab das jetzt mittels SET NAMES und SET CHARACTER SET umgestellt auf utf8.

        Bitte informiere dich über die Arbeitsweisen beider Anweisungen im MySQL-Handbuch-Abschnitt Connection Character Sets and Collations und verwende dann ausschließlich SET NAMES. Beide Anweisungen heben sich gegenseitig auf und SET CHARACTER SET möchte man normalerweise auch dann nicht haben, wenn man deren Wirkungsweise verstanden hat. Den Rest des Kapitels Character Set Support solltest du auch nicht ignorieren.

        echo "$verabschiedung $name";

    2. echo $begrüßung;

      so, heute ist mir aufgefallen, dass ich doch ein Problem mit den Zeichen und insbesondere den Umlauten habe.

      Auch wenn du das Problem schon gelöst hast, gestatte ich mir einige Anmerkungen.

      -- MySQL-Tabellen haben Character set UTF-8

      Die Kodierungsangabe der Tabelle ist nicht so wichtig. Das ist nur ein Defaultwert, der für neue Felder verwendet wird, wenn für diese keine explizite Angabe gemacht wurde. Mit welcher Kodierung die Daten in den Feldern abgelegt werden sollen bestimmt deren Kodierungsangabe.

      -- Webseite ist mit Meta-Tag als UTF-8 gekennzeichnet

      Ein gleichnamiger HTTP-Header überschreibt diese Angabe. Das Meta-Element sollte trotzdem enthalten sein, denn gelegentlich speichert man Dokumente lokal ab, und dann ist kein HTTP-Header mehr vorhanden.

      -- Daten werden ohne Bearbeitung/Umwandlung so wie sie von der Webseite kommen in die Tabellen gespeichert,

      Das ist nicht richtig. MySQL wandelt gegebenenfalls die Kodierung der Daten, wenn die Kodierungsangabe der Felder nicht mit der Angabe der aktuellen Verbindung übereinstimmt.

      über PHPMyAdmin werden Umlaute etc. richtig angezeigt (Kodierung im Browser UTF-8, ebenso PHPMyAdmin)

      Wenn der PMA die Daten richtig anzeigt, kann man im Allgemeinen davon ausgehen, dass sie im DBMS richtig kodiert drinstehen.

      -- Daten aus der Datenbank werden in der Webseite auch unverändert angezeigt (also ohne die Kodierung zu verändern meine ich),

      siehe zwei Punte weiter oben

      Die Webseiten tragen jetzt utf-8 als meta-tag Kodierung und plötzlich sind alle Umlaute falsch dargestellt, in Firefox erscheint jetzt dieses Fragezeichen-Symbol.

      "Falsch dargestellt" werden kann etwas auf vielfältige Art und Weise. Wenn du der Ursache auf den Grund gehen willst, solltest du genauer beobachten und beschreiben was passiert. Bei Kodierungsproblemen empfiehlt es sich, die Bytewerte anzusehen.

      Wenn ich in ein Formular z. B. ä eingebe und an den Server zum Speichern in der Tabelle schicke, dann steht in der Tabelle (über PHPMyAdmin mit Kodierung UTF-8) kein lesbares ä mehr drinnen, sondern "Müll", also unleserliches Zeichen anstelle des ä.
      Jemand einen Tipp wo ich ansetze um die Kodierung wieder passend zu machen, [...]?

      Liest du überhaupt die Anworten, die dir gegeben werden? Der Hinweis auf SET NAMES kam doch schon mit der ersten Antwort auf deine Ursprungsfrage.

      echo "$verabschiedung $name";