MySQL utf8mb4, Fehler beim Speichern von Emojis

77

Ich versuche, Namen von Benutzern eines Dienstes in meiner MySQL-Datenbank zu speichern. Diese Namen können Emojis wie 🙈😂😱🍰 enthalten (nur für Beispiele)

Nachdem ich ein wenig gesucht hatte, fand ich diesen Stapelüberlauf , der mit diesem Tutorial verknüpft war . Ich habe die Schritte befolgt und es sieht so aus, als ob alles richtig konfiguriert ist.

Ich habe eine Datenbank (Zeichensatz und Sortierung auf utf8mb4 (_unicode_ci) eingestellt), eine ebenfalls so konfigurierte Tabelle namens TestTable sowie eine auf diese Weise konfigurierte Spalte "Text" (VARCHAR (191) utf8mb4_unicode_ci).

Wenn ich versuche, Emojis zu speichern, wird folgende Fehlermeldung angezeigt:

Example of error for shortcake (🍰):
    Warning: #1300 Invalid utf8 character string: 'F09F8D'
    Warning: #1366 Incorrect string value: '\xF0\x9F\x8D\xB0' for column 'Text' at row 1

Das einzige Emoji, das ich richtig retten konnte, war die Sonne ☀️

Obwohl ich nicht alle ausprobiert habe, um ehrlich zu sein.

Fehlt mir etwas in der Konfiguration?

Bitte beachten Sie: An allen Speichertests war keine Client-Seite beteiligt. Ich benutze phpmyadmin, um die Werte manuell zu ändern und die Daten zu speichern. Die richtige Konfiguration der Client-Seite ist etwas, um das ich mich kümmern werde, nachdem der Server Emojis richtig gespeichert hat.

Noch eine Nebenbemerkung : Derzeit erhalte ich beim Speichern von Emojis entweder den oben beschriebenen Fehler oder erhalte keinen Fehler und die Daten von Username 🍰werden als gespeichert Username ????. Fehler oder kein Fehler hängen davon ab, wie ich speichere. Beim Erstellen / Speichern über eine SQL-Anweisung speichere ich mit Fragezeichen, beim Bearbeiten inline speichere ich mit Fragezeichen, beim Bearbeiten mit der Schaltfläche Bearbeiten erhalte ich den Fehler.

Vielen Dank

EDIT 1: Okay, ich denke, ich habe das Problem herausgefunden, aber nicht die Lösung. Es sieht so aus, als hätten sich die datenbankspezifischen Variablen nicht richtig geändert.

Wenn ich auf meinem Server als root angemeldet bin und die Variablen auslese (global):
Verwendete Abfrage:SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';

+--------------------------+--------------------+
| Variable_name            | Value              |
+--------------------------+--------------------+
| character_set_client     | utf8mb4            |
| character_set_connection | utf8mb4            |
| character_set_database   | utf8mb4            |
| character_set_filesystem | binary             |
| character_set_results    | utf8mb4            |
| character_set_server     | utf8mb4            |
| character_set_system     | utf8               |
| collation_connection     | utf8mb4_unicode_ci |
| collation_database       | utf8mb4_unicode_ci |
| collation_server         | utf8mb4_unicode_ci |
+--------------------------+--------------------+
10 rows in set (0.00 sec)

Für meine Datenbank (in phpmyadmin dieselbe Abfrage) sieht es folgendermaßen aus:

+--------------------------+--------------------+
| Variable_name            | Value              |
+--------------------------+--------------------+
| character_set_client     | utf8               |
| character_set_connection | utf8mb4            |
| character_set_database   | utf8mb4            |
| character_set_filesystem | binary             |
| character_set_results    | utf8               |
| character_set_server     | utf8               |
| character_set_system     | utf8               |
| collation_connection     | utf8mb4_unicode_ci |
| collation_database       | utf8mb4_unicode_ci |
| collation_server         | utf8mb4_unicode_ci |
+--------------------------+--------------------+

Wie kann ich diese Einstellungen für die jeweilige Datenbank anpassen? Auch wenn ich die ersten angezeigten Einstellungen als Standard habe, erhalte ich beim Erstellen einer neuen Datenbank die zweite als Einstellungen.

Bearbeiten 2:

Hier ist meine my.cnfDatei:

[client]
port=3306
socket=/var/run/mysqld/mysqld.sock
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

[mysqld_safe]
socket=/var/run/mysqld/mysqld.sock

[mysqld]
user=mysql
pid-file=/var/run/mysqld/mysqld.pid
socket=/var/run/mysqld/mysqld.sock
port=3306
basedir=/usr
datadir=/var/lib/mysql
tmpdir=/tmp
lc-messages-dir=/usr/share/mysql
log_error=/var/log/mysql/error.log
max_connections=200
max_user_connections=30
wait_timeout=30
interactive_timeout=50
long_query_time=5
innodb_file_per_table
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

!includedir /etc/mysql/conf.d/
Loki
quelle
1
Es ist ein phpmyadmin Problem, versuchen Sie es mit einem anderen MySQL-Client.
jsxqf
2
Ich denke nicht, dass es ein phpmyadmin-Problem ist. Wie Sie in Edit1 sehen können, ist es meiner Meinung nach eine Fehlkonfiguration zwischen conf / default-Variablen / -Parametern und denen in der Datenbank. Auch wenn beim Erstellen einer neuen Datenbank.
Loki
Was ist $cfg["DefaultCharset"]in Ihrer PMA-Konfiguration?
miken32
1
Ich habe nicht gefunden $cfg["DefaultCharset"]. Ich habe es nachgeschlagen etc/phpmyadmin/config.inc.php. Nicht da drin.
Loki
@jsxqf Hallo zusammen, nach einer Weile und dem Wiederherstellen des gesamten "Tutorials" wurde mir klar, dass es sich akut um ein MySQL-Problem handelte. Die Sitzungsvariablen unterschieden sich von den globalen Variablen. Eine neue Verbindung, die bei Verwendung meiner API zustande kommt, verwendet die globalen Variablen und funktioniert :). Wenn Sie also eine vollständige Antwort geben, werde ich sie akzeptieren und Sie erhalten das Kopfgeld. Darüber hinaus würde ich mich freuen, wenn Sie auch angeben könnten, wie ich die Sitzungsvariablen von phpmyadmins zurücksetzen kann. Ich habe das nicht zum Laufen gebracht. Sie sind immer noch falsch eingestellt.
Loki

Antworten:

94

character_set_client, _connectionUnd _resultssein müssen alle utf8mb4für das shortcake essbar zu sein.

Irgendwo setzt etwas eine Teilmenge davon einzeln. Stöbern Sie in den Einstellungen von my.cnf und phpmyadmin - etwas setzt nicht alle drei.

Wenn SET NAMES utf8mb4ausgeführt, werden alle drei korrekt eingestellt.

Die Sonne schien, weil es nur 3 Bytes sind - E2 98 80; utf8 reicht für 3-Byte-utf8-Codierungen von Unicode-Zeichen aus.

Rick James
quelle
Okay, ich denke das bringt mich näher. Vielen Dank. Ich habe meine Frage bearbeitet und die my.cnf hinzugefügt. Vielleicht können Sie dort etwas sehen?
Loki
1
Die Verbindung muss haben utf8mb4. Wenn Sie nicht finden, wo Sie das tun sollen, führen Sie es aus SET NAMES utf8mb4.
Rick James
Dies ist eine schöne Erklärung dafür, was auch schief gelaufen ist. Aber zusätzlich musste ich die Sitzung und die globalen Variablen überprüfen. Realisieren von PHPMyadmins Sitzungsvariablen waren immer noch falsch und der Fehler trat nur für das Admin Board auf.
Loki
2
Vielen Dank. mysql_query("SET NAMES 'utf8mb4'");das ist richtig;)
mghhgm
1
Oh, ich habe einen verpasst - Ein Shortcake ist groß genug für 4 Bytes.
Rick James
7

Es ist wahrscheinlich, dass Ihr Dienst / Ihre Anwendung für den Client-Zeichensatz eine Verbindung mit "utf8" anstelle von "utf8mb4" herstellt. Das liegt an der Client-Anwendung.

Eine PHP-Anwendung finden Sie unter http://php.net/manual/en/function.mysql-set-charset.php oder http://php.net/manual/en/mysqli.set-charset.php

Eine Python-Anwendung finden Sie unter https://github.com/PyMySQL/PyMySQL#example oder http://docs.sqlalchemy.org/en/latest/dialects/mysql.html#mysql-unicode

Überprüfen Sie auch, ob Ihre Spalten wirklich utf8mb4 sind. Ein direkter Weg ist wie folgt:

mysql> SELECT character_set_name FROM information_schema.`COLUMNS`  WHERE table_name = "user"   AND column_name = "displayname";
+--------------------+
| character_set_name |
+--------------------+
| utf8mb4            |
+--------------------+
1 row in set (0.00 sec)
Pierce
quelle
7

Für mich stellte sich heraus, dass das Problem im MySQL-Client lag.

Die Zeicheneinstellung des MySQL-Clients aktualisiert sich my.cnfauf einem Server und führt zu einer unbeabsichtigten Zeicheneinstellung.

Also musste ich nur hinzufügen character-set-client-handshake = FALSE. Dadurch wird verhindert, dass die Client-Einstellung meine Zeicheneinstellung stört.

my.cnf wäre so.

[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
...

Ich hoffe es hilft.

user3624198
quelle
0

ALTER TABLE table_nameCHANGE column_name column_name VARCHAR (255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;

Beispielabfrage:

ALTER TABLE `reactions` CHANGE `emoji` `emoji` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;

Geben Sie hier die Bildbeschreibung ein

danach erfolgreich in der Lage, Emoji in Tabelle zu speichern:

Geben Sie hier die Bildbeschreibung ein

Saurabh Mistry
quelle
0

Erwägen Sie das Hinzufügen

init_connect = 'SET NAMES utf8mb4'

an alle my.cnf-s Ihrer Datenbankserver.

(dennoch können (werden) Kunden es außer Kraft setzen)

druid62
quelle
-1

Ich bin nicht stolz auf diese Antwort, weil sie Brute-Force verwendet, um die Eingabe zu bereinigen. Es ist brutal, aber es funktioniert

function cleanWord($string, $debug = false) {
    $new_string = "";

    for ($i=0;$i<strlen($string);$i++) {
        $letter = substr($string, $i, 1);
        if ($debug) {
            echo "Letter: " . $letter . "<BR>";
            echo "Code: " . ord($letter) . "<BR><BR>";
        }
        $blnSkip = false;
        if (ord($letter)=="146") {
            $letter = "&acute;";
            $blnSkip = true;
        }
        if (ord($letter)=="233") {
            $letter = "&eacute;";
            $blnSkip = true;
        }
        if (ord($letter)=="147" || ord($letter)=="148") {
            $letter = "&quot;";
            $blnSkip = true;
        }
        if (ord($letter)=="151") {
            $letter = "&#8211;";
            $blnSkip = true;
        }
        if ($blnSkip) {
            $new_string .= $letter;
            break;
        }

        if (ord($letter) > 127) {
            $letter = "&#0" . ord($letter) . ";";
        }

        $new_string .= $letter;
    }
    if ($new_string!="") {
        $string = $new_string;
    }
    //optional
    $string = str_replace("\r\n", "<BR>", $string);

    return $string;
}

//clean up the input
$message = cleanWord($message);

//now you can insert it as part of SQL statement
$sql = "INSERT INTO tbl_message (`message`)
VALUES ('" . addslashes($message) . "')";
Nicolas Giszpenc
quelle