MySQL utf8mb4, Помилки при збереженні Emojis


77

Я намагаюся зберегти імена від користувачів із сервісу в базі даних MySQL. Ці імена можуть містити смайли типу 🙈😂😱🍰 (лише для прикладів)

Трохи пошукавши, я знайшов цей stackoverflow, що посилається на цей підручник . Я дотримувався кроків, і, схоже, все налаштовано належним чином.

У мене є База даних (набір символів та сортування utf8mb4 (_unicode_ci)), Таблиця під назвою TestTable, також налаштована таким чином, а також стовпець "Текст", налаштований таким чином (VARCHAR (191) utf8mb4_unicode_ci).

Коли я намагаюся зберегти смайли, я отримую повідомлення про помилку:

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

Єдиним смайликом, який я зміг зберегти належним чином, було сонце ☀️

Хоча я не намагався, щоб усі вони були чесними.

Щось мені не вистачає в конфігурації?

Зверніть увагу: усі тести збереження не залучали сторону клієнта. Я використовую phpmyadmin, щоб вручну змінювати значення та зберігати дані. Отже, правильна конфігурація на стороні клієнта - це те, про що я подбаю після того, як сервер правильно збереже смайлики.

Ще один Sidenote : В даний час при збереженні смайлів я або отримую помилку, як зазначено вище, або не отримую жодної помилки, і дані Username 🍰будуть зберігатися як Username ????. Помилка або відсутність помилки залежить від способу збереження. При створенні / збереженні за допомогою SQL Statement я зберігаю зі знаками запитання, при редагуванні в рядку - із знаками питання, при редагуванні за допомогою кнопки редагування отримую помилку.

Дякую

РЕДАКТУВАТИ 1: Добре, отже, я думаю, що з’ясував проблему, але не вирішив її. Схоже, специфічні змінні бази даних не змінились належним чином.

Коли я ввійшов як сервер root на своєму сервері і прочитав змінні (глобальні):
Використовуваний запит: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)

Для моєї бази даних (у phpmyadmin, той самий запит) це виглядає так:

+--------------------------+--------------------+
| 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 |
+--------------------------+--------------------+

Як я можу налаштувати ці параметри для конкретної бази даних? Крім того, хоча перші налаштування відображаються за замовчуванням, при створенні нової бази даних я отримую другу як налаштування.

Редагувати 2:

Ось мій my.cnfфайл:

[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/

1
це проблема phpmyadmin, спробуйте інший клієнт mysql.
jsxqf

2
Я не думаю, що це проблема phpmyadmin. Як ви можете бачити в Edit1, я думаю, що це якась неправильна конфігурація між змінними / параметрами conf / за замовчуванням та тими, що знаходяться в базі даних. Навіть при створенні нової бази даних.
Локі

Що $cfg["DefaultCharset"]у вашій конфігурації PMA?
miken32

1
Я не знайшов $cfg["DefaultCharset"]. Я шукав це в etc/phpmyadmin/config.inc.php. Не там.
Локі

@jsxqf Привіт, через деякий час і переробивши весь "підручник", я зрозумів, що це гостро - проблема MySQL. Змінні сеансу відрізнялися від загальних змінних. Нове з'єднання, яке відбувається при використанні мого API, використовує глобальні змінні і працює :). Тож насправді, якщо ви надасте повну відповідь, я прийму її, і ви отримаєте нагороду. Крім того, крім цього, я був би вдячний, якщо б ви також могли сказати, як я можу скинути змінні сеансу phpmyadmins. Я не змусив це працювати. Вони все ще неправильно налаштовані.
Локі

Відповіді:


94

character_set_client, _connectionІ _resultsвсе повинні бути utf8mb4для цього пісочного бути їстівним.

Щось десь десь встановлює підмножину цих індивідуально. Перебирайте налаштування my.cnf та phpmyadmin - щось не встановлює всі три.

Якщо SET NAMES utf8mb4виконано, усі три встановлено правильно.

Сонце світило, бо воно всього 3 байти - E2 98 80; utf8 достатньо для 3-байтового кодування utf8 символів Unicode.


Гаразд, я думаю, це мене зближує. Дякую. Я відредагував своє запитання та додав файл my.cnf. Може, ви можете щось там побачити?
Локі

1
Зв'язок повинен бути utf8mb4. Якщо ви не можете знайти, де це зробити, виконайте SET NAMES utf8mb4.
Рік Джеймс,

Це гарне пояснення того, що теж пішло не так. Але на додаток мені довелося перевірити сеанс та глобальні змінні. Зрозумівши, що змінні сеансу PHPMyadmins все ще були неправильними, і помилка траплялася лише для адміністративної дошки.
Локі

2
Дякую. mysql_query("SET NAMES 'utf8mb4'");це правильно;)
mghhgm

1
О, я пропустив один - пісочний пиріг достатній для 4 байт.
Рік Джеймс,

8

Для мене виявилося, що проблема криється в клієнті mysql.

Клієнт mysql оновлює my.cnfпараметр char на сервері, що призвело до ненавмисного налаштування символів.

Отже, мені потрібно було лише додати character-set-client-handshake = FALSE. Це відключає налаштування клієнта від порушення мого налаштування символу.

my.cnf було б так.

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

Сподіваюся, це допоможе.


7

Ймовірно, що ваша послуга / програма підключається до "utf8" замість "utf8mb4" для набору символів клієнта. Це залежить від клієнтської програми.

Про програму PHP див. Http://php.net/manual/en/function.mysql-set-charset.php або http://php.net/manual/en/mysqli.set-charset.php

Про програму Python див. Https://github.com/PyMySQL/PyMySQL#example або http://docs.sqlalchemy.org/en/latest/dialects/mysql.html#mysql-unicode

Також перевірте, чи є ваші стовпці справді utf8mb4. Один прямий шлях такий:

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)

0

table_nameЗМІНИТИ ТАБЛИЦЮ column_name column_name ЗМІНИТЬ VARCHAR (255) НАБІР ХАРАКТЕРІВ utf8mb4 ЗБІРИТИ utf8mb4_unicode_ci НУЛЬ ПОЗНАЧАННЯ НУЛЬ;

приклад запиту:

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

введіть тут опис зображення

після цього вдало зберегти смайли в таблиці:

введіть тут опис зображення


0

Подумайте про додавання

init_connect = 'SET NAMES utf8mb4'

на всі ваші db-сервери my.cnf-s.

(все-таки клієнти можуть (так буде) це скасувати)


-1

Я не пишаюся цією відповіддю, оскільки вона використовує грубу силу для очищення вводу. Це жорстоко, але це працює

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) . "')";
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.