Probleme mit UTF-8-Zeichen; Was ich sehe, ist nicht das, was ich gespeichert habe

76

Ich habe versucht, UTF-8 zu verwenden, und bin auf Probleme gestoßen.

Ich habe so viele Dinge versucht; Hier sind die Ergebnisse, die ich erhalten habe:

  • ????anstelle von asiatischen Zeichen. Auch für europäischen Text habe ich Se?orfür Señor.
  • Seltsamer Kauderwelsch (Mojibake?) Wie Señoroder 新浪新闻für 新浪新闻.
  • Schwarze Diamanten wie Se or.
  • Schließlich geriet ich in eine Situation, in der die Daten verloren gingen oder zumindest abgeschnitten wurden: Sez Señor.
  • Selbst wenn ich den Text richtig aussehen ließ , wurde er nicht richtig sortiert .

Was mache ich falsch? Wie kann ich den Code reparieren ? Kann ich die Daten wiederherstellen , wenn ja, wie?

Rick James
quelle

Antworten:

132

Dieses Problem plagt die Teilnehmer dieser Site und viele andere.

Sie haben die fünf Hauptprobleme aufgelistet CHARACTER SET.

Beste Übung

In Zukunft ist es am besten, CHARACTER SET utf8mb4und zu verwenden COLLATION utf8mb4_unicode_520_ci. (Es ist eine neuere Version der Unicode-Sortierung in der Pipeline.)

utf8mb4ist eine Obermenge davon, utf8dass es 4-Byte-utf8-Codes verarbeitet, die von Emoji und einigen Chinesen benötigt werden.

Außerhalb von MySQL bezieht sich "UTF-8" auf alle Größencodierungen, daher praktisch die gleichen wie bei MySQL utf8mb4, nicht utf8.

Ich werde versuchen, diese Schreibweisen und Großschreibungen zu verwenden, um im Folgenden innerhalb und außerhalb von MySQL zu unterscheiden.

Überblick darüber, was Sie tun sollten

  • Stellen Sie Ihren Editor usw. auf UTF-8 ein.
  • HTML-Formulare sollten wie folgt beginnen <form accept-charset="UTF-8">.
  • Lassen Sie Ihre Bytes als UTF-8 codieren.
  • Richten Sie UTF-8 als die im Client verwendete Codierung ein.
  • Lassen Sie die Spalte / Tabelle deklarieren CHARACTER SET utf8mb4( überprüfen mit SHOW CREATE TABLE.)
  • <meta charset=UTF-8> am Anfang von HTML
  • Gespeicherte Routinen erfassen den aktuellen Zeichensatz / die aktuelle Sortierung. Sie müssen möglicherweise neu aufgebaut werden.

UTF-8 bis zum Ende

Weitere Details zu Computersprachen (und den folgenden Abschnitten)

Testen Sie die Daten

Das Anzeigen der Daten mit einem Tool oder mit SELECTkann nicht als vertrauenswürdig eingestuft werden. Zu viele solcher Clients, insbesondere Browser, versuchen, falsche Codierungen zu kompensieren und zeigen Ihnen korrekten Text an, selbst wenn die Datenbank beschädigt ist. Wählen Sie also eine Tabelle und eine Spalte mit nicht englischem Text aus und tun Sie dies

SELECT col, HEX(col) FROM tbl WHERE ...

Das HEX für korrekt gespeichertes UTF-8 ist

  • Für ein Leerzeichen (in einer beliebigen Sprache): 20
  • Für Englisch: 4x, 5x, 6x, oder7x
  • Für den größten Teil Westeuropas sollten Buchstaben mit Akzent verwendet werden Cxyy
  • Kyrillisch, Hebräisch und Persisch / Arabisch: Dxyy
  • Der größte Teil Asiens: Exyyzz
  • Emoji und einige Chinesen: F0yyzzww
  • Mehr Details

Spezifische Ursachen und Lösungen für die aufgetretenen Probleme

Abgeschnittener Text ( Sefür Señor):

  • Die zu speichernden Bytes werden nicht als utf8mb4 codiert. Repariere das.
  • Überprüfen Sie außerdem, ob die Verbindung während des Lesens UTF-8 ist.

Schwarze Diamanten mit Fragezeichen ( Se�orfür Señor); Einer dieser Fälle liegt vor:

Fall 1 (ursprüngliche Bytes waren nicht UTF-8):

  • Die zu speichernden Bytes werden nicht als utf8 codiert. Repariere das.
  • Die Verbindung (oder SET NAMES) für das INSERT und das SELECTwar nicht utf8 / utf8mb4. Repariere das.
  • Überprüfen Sie außerdem, ob die Spalte in der Datenbank CHARACTER SET utf8(oder utf8mb4) lautet.

Fall 2 (ursprüngliche Bytes waren UTF-8):

  • Die Verbindung (oder SET NAMES) für das SELECTwar nicht utf8 / utf8mb4. Repariere das.
  • Überprüfen Sie außerdem, ob die Spalte in der Datenbank CHARACTER SET utf8(oder utf8mb4) lautet.

Schwarze Diamanten treten nur auf, wenn der Browser auf eingestellt ist <meta charset=UTF-8>.

Fragezeichen (normale, keine schwarzen Diamanten) ( Se?orfür Señor):

  • Die zu speichernden Bytes werden nicht als utf8 / utf8mb4 codiert. Repariere das.
  • Die Spalte in der Datenbank ist nicht CHARACTER SET utf8(oder utf8mb4). Repariere das. (Verwenden SHOW CREATE TABLE.)
  • Überprüfen Sie außerdem, ob die Verbindung während des Lesens UTF-8 ist.

Mojibake ( Señorfür Señor): (Diese Diskussion gilt auch für die Doppelkodierung , die nicht unbedingt sichtbar ist.)

  • Die zu speichernden Bytes müssen UTF-8-codiert sein. Repariere das.
  • Die Verbindung wann INSERTingund SELECTingText muss utf8 oder utf8mb4 angeben. Repariere das.
  • Die Spalte muss deklariert werden CHARACTER SET utf8(oder utf8mb4). Repariere das.
  • HTML sollte mit beginnen <meta charset=UTF-8>.

Wenn die Daten korrekt aussehen, aber nicht korrekt sortiert werden, haben Sie entweder die falsche Sortierung ausgewählt oder es gibt keine Kollatierung, die Ihren Anforderungen entspricht, oder Sie haben die doppelte Codierung .

Die doppelte Codierung kann durch Ausführen der SELECT .. HEX ..oben beschriebenen Schritte bestätigt werden .

é should come back C3A9, but instead shows C383C2A9
The Emoji 👽 should come back F09F91BD, but comes back C3B0C5B8E28098C2BD

Das heißt, das Hex ist ungefähr doppelt so lang wie es sein sollte. Dies wird verursacht, indem von latin1 (oder was auch immer) nach utf8 konvertiert wird, diese Bytes dann so behandelt werden, als wären sie latin1, und die Konvertierung wiederholt wird. Das Sortieren (und Vergleichen) funktioniert nicht richtig, da beispielsweise so sortiert wird, als ob die Zeichenfolge wäre Señor.

Daten nach Möglichkeit korrigieren

Bei Kürzungen und Fragezeichen gehen die Daten verloren.

Für Mojibake / Double Encoding ...

Für schwarze Diamanten ...

Die Fixes sind hier aufgelistet. (5 verschiedene Korrekturen für 5 verschiedene Situationen; sorgfältig auswählen): http://mysql.rjweb.org/doc.php/charcoll#fixes_for_various_cases

Rick James
quelle
Wenn der Client, die Datenbank und die Tabellen vorhanden sind, kann utf8mb4ich Emojis problemlos speichern. Einige Blogs empfehlen auch das Einstellen collation-serverund character-set-serverin mysqld. Muss ich wirklich ändern, mysqldwelchen Unterschied die Servereinstellungen machen?
David_adler
@david_adler - Es gibt mehrere Möglichkeiten, um die Wirkung dieser Einstellungen zu erzielen. Am besten verwenden Sie die Verbindungsparameter des Clients. Zweitbeste durch Ausführung SET NAMES utf8mb4direkt nach dem Verbinden. Dies deklariert schließlich die Codierung im Client .
Rick James
Bei MySQL 8.0 (jetzt veröffentlicht) ist der Standardwert utf8mb4und utf8mb4_0900_ai_ci. Die meisten Benutzer sollten sie verwenden, ohne die anderen Zeichensätze und Sortierungen zu berücksichtigen.
Rick James
Tipps zur Konfiguration von Python , PHP und etwa 40 anderen Sprachen
Rick James
Noch ein Hinweis: Wenn ein FUNCTIONoder STORED PROCEDUREbeteiligt ist, haben Sie möglicherweise nicht den gewünschten Zeichensatz verwendet, als Sie ihn erstellt haben. DROPes , SET NAMES; Re CREATEit.
Rick James
6

Nach einer Servermigration hatte ich ähnliche Probleme mit zwei meiner Projekte. Nachdem ich viele Lösungen gesucht und ausprobiert hatte, stieß ich auf diese:

mysqli_set_charset($con,"utf8");

Nach dem Hinzufügen dieser Zeile zu meiner Konfigurationsdatei funktioniert alles einwandfrei!

Ich habe diese Lösung für mysqli https://www.w3schools.com/PHP/func_mysqli_set_charset.asp gefunden, als ich eine Einfügung aus einer HTML-Abfrage lösen wollte

Viel Glück!

castro_pereira
quelle
Ja, das ist eines von mehreren Dingen, die Probleme mit dem Zeichensatz verursachen können. Hinweis: Diese Syntax gilt für PHP, nicht für andere App-Sprachen, und nur bei Verwendung mysqli, nicht PDO.
Rick James
1

Lustig, wie du deine eigene Frage beantwortest :)

  1. Stellen Sie Ihre Code-IDE-Sprache auf UTF8 ein

  2. Fügen Sie Ihrem Webseiten-Header hinzu, in dem Sie Daten sammeln.

  3. Überprüfen Sie, ob Ihre MySQL-Tabellendefinition folgendermaßen aussieht:

    CREATE TABLE your_table (
      ...
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    
  4. Wenn Sie PDO verwenden, stellen Sie sicher

    $options = array(PDO::MYSQL_ATTR_INIT_COMMAND=>'SET NAMES utf8'); 
    $dbL = new PDO($pdo, $user, $pass, $options);
    

Wenn Sie bereits eine große Datenbank mit dem oben genannten Problem haben, können Sie versuchen, SIDU mit dem richtigen Zeichensatz zu exportieren und mit UTF8 zurück zu importieren. Viel Glück

SIDU
quelle
8
(Die Beantwortung der eigenen Frage ist ein Merkmal dieses Forums.) Ich arbeite seit Jahren daran, die Antwort so kurz und doch vollständig zu machen.
Rick James
Das DEFAULT CHARSETfür eine Tabelle ist genau das, eine Standardeinstellung. Es kann und sollte manchmal in der Spaltendefinition überschrieben werden.
Rick James
2
PDO wird besser mit der Zeichensatzoption durchgeführt: $db = new PDO('dblib:host=host;dbname=db;charset=UTF8', $user, $pwd); (Dies ist im Link zu meinem 'charcoll'-Dokument aufgeführt.)
Rick James
Sie sind 20K erfahrener als ich :) Ja, Sie können einen Zeichensatz für eine Spalte festlegen. Versuchen Sie, es nicht zu stark zu verwenden. Am Ende haben Sie mehr Verwaltungszeit. Ebenso können Sie Zugriff auf eine bestimmte Spalte einer MySQL-Tabelle gewähren. Sie müssen es jedoch nicht verwenden, es sei denn, Sie haben keine bessere Alternative.
SIDU
2
@ppmakeitcount: Nein, die ALTER DATABASEAnweisung erfordert keinen Neustart von MySQL, um wirksam zu werden. Das Ändern des Standardzeichensatzes für eine Datenbank wirkt sich jedoch nicht auf Tabellen aus, die sich derzeit in der Datenbank befinden. Dies wirkt sich nur auf neue Tabellen aus, z. B. CREATE TABLEdie keinen Standardzeichensatz für die Tabelle angeben. Dann kommt der Standard-Zeichensatz der Datenbank ins Spiel. (In ähnlicher Weise wirkt sich das Ändern des Standardzeichensatzes der Tabelle nicht auf Spalten aus, die bereits in der Tabelle enthalten sind.
Dies
1

Ich suchte auch nach dem gleichen Problem. Ich brauchte fast einen Monat, um die passende Lösung zu finden. Zunächst müssen Sie Ihre Datenbank mit allen aktuellen CHARACTER und COLLATION auf utf8mb4 oder mindestens aktualisieren, die utf-8-Daten unterstützen.

Für Java:

Wenn Sie eine JDBC-Verbindung herstellen, fügen Sie diese zur Verbindungs-URL hinzu. Verwenden Sie Unicode = yes & characterEncoding = UTF-8 als Parameter, und es funktioniert.

Für Python:

Versuchen Sie vor dem Abfragen in der Datenbank, dies über den Cursor zu erzwingen * cursor.execute('SET NAMES utf8mb4') cursor.execute("SET CHARACTER SET utf8mb4") cursor.execute("SET character_set_connection=utf8mb4") *

Wenn es nicht funktioniert, suchen Sie gerne nach der richtigen Lösung.

Ashish Bhatt
quelle
1 Monat? Das war schnell. Ich habe mehr als ein Jahr gebraucht, um diese Fragen und Antworten zu formulieren. Java sieht richtig aus. SETssind nicht der 'richtige' Weg für Python; siehe mysql.rjweb.org/doc.php/charcoll#python Viele andere Sprachen werden an anderer Stelle in diesem Blog behandelt.
Rick James
@RickJames Dieses Problem tritt jedoch bei MySQL-Python unter 1.2.4 auf, sodass die SETAnweisungen im Grunde genommen eine Problemumgehung darstellen .
Ashish Bhatt
-3

Je nachdem, wie der Server eingerichtet ist, müssen Sie die Codierung entsprechend ändern. utf8 von dem, was Sie gesagt haben, sollte am besten funktionieren, aber wenn Sie seltsame Zeichen bekommen, kann es hilfreich sein, wenn Sie die Webseite Encode in Ansi ändern. Dies hat mir beim Einrichten eines PHP-MYSQLI geholfen. Dies könnte Ihnen helfen, mehr über /superuser/762473/ansi-to-utf-8-in-notepad zu erfahren

paul
quelle
Notepad's ANSIist wahrscheinlich MySQL's am nächsten latin1. Die 0x93 in diesem Link ist und stammt wahrscheinlich von einem Ort wie Word. Sie können entweder in utf8 (hex E2809C) konvertieren oder MySQL mitteilen, dass die Daten vorhanden sind, latin1und hoffen, dass Sie nicht woanders stolpern.
Rick James