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.