MySQL → emoji, MySQL и кодировка UTF-8

Давно уже, многие годы, знал о проблеме, что комментарии в этом блоге с эмоджами не сохраняются, как и статьи, потому что MySQL ругался чем-то вроде Incorrect string value: '\xF0\x9F\x87\xB2...' for column 'text' at row 1 и записи не сохранял. Но исправить эту штуку было просто недосуг, комментируют здесь не то, чтобы сильно часто, да и то чаще боты со спамом. А вот в новой версии движка блога, которая уже фактически запущена, это было запланировано к исправлению 😁

Когда переносил блог на новый сервер, то установил там восьмую версию мускула и проверил, сохраняет или нет. Вдруг разработчики базы данных давно уже исправили этот incorrect string value, я ведь не один такой на планете с эмоджами, нас десятки и сотни тысяч, уверен, если не миллионы. Но результат не поменялся, та же ошибка. Не прокатило, штош...

Собственно, давно уже придумал как это сделать. Точно так же я давно уже размещал эмоджи в статьях, а именно: смотрел в emojipedia, пусть будет зелёное сердце, например, 💚. В эмоджипедии вижу, что это сердце имеет в юникоде кодовую позицию U+1F49A, далее вставляю HTML-спецсимвол 💚 и готово. Оставалось только автоматизировать этот процесс. Решил только загуглить диапазон проблемных для MySQL символов, чтобы не конвертировать все подряд многобайтное, включая кириллицу.

Разочарованию не было предела, я уже настроился на программирование, а проблема с сохранением решается просто изменением кодировки. Так же становится и очевидным, почему вообще такое происходит. utf8 в MySQL является псевдонимом utf8mb3, т.е. корректными символами являются мультибайтные символы UTF-8 до 3-х байт включительно. А стандарт кодирования UTF-8 предусматривает использование от одного до четырёх байт.

1
2
3
4
5
6
# для начала меняем кодировку и метод сравнения для базы данных
ALTER DATABASE db_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

# потом меняем кодировку для каждой таблицы, где это необходимо
ALTER TABLE db_name.table_name_1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE db_name.table_name_2 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Выше запросы к БД для изменения кодировки, заодно изменяется и метод сравнения текстовых строк, поскольку utf8_general_ci не учитывает порядок букв и для него Ä = A, Ü = U, ß = s и т.д. (Хотя если тексты у вас только на английском или русском, то разницы не будет никакой, а по слухам utf8_general_ci работает быстрее utf8_unicode_ci, вдруг у вас бигдата и эта разница будет даже заметна на глаз)

На этом можно было бы и закончить данную заметку, но раз уже упомянул в заголовке и кодировку UTF-8, то напишу пару слов и за неё. Вот таблица, показывающая как кодируются символы:

Шаблон Значащих бит Диапазон кодовых позиций Unicode
0....... 7 0 - 7F
110..... 10...... 11 80 - 7FF
1110.... 10...... 10...... 16 800 - FFFF
11110... 10...... 10...... 10...... 21 10000 - 10FFFF

Вместо точек, которые видно в таблице выше, подставляются биты кодовой позиции юникод-символа, а старшие биты октетов (байт) UTF-8 символа позволяют определить начало многобайтной последовательности и её длину, по числу повторяющихся единиц в старших битах первого октета. У однобайтных символов получается полное соответствие с кодировкой ASCII

Продемонстрирую на примере зелёного сердца из начала статьи.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
💚 → U+1F49A

# В двоичном виде
0x1F49A (hex)11111010010011010 (binary)

# В виде байт закодированного в UTF-8 символа
11110000 10011111 10010010 10011010

# Или в более привычном виде
0xF0 0x9F 0x92 0x9A

Так что ничего магического в кодировке UTF-8 нет и если бы в MySQL не оказалось utf8mb4, то можно бы было выбирать из текста 4-хбайтные символы и преобразовывать их в HTML-сущности. Но не пришлось 🙂

Комментарии

0 комментариев Написать что-нибудь
Или войдите, чтобы не заполнять форму:
Адрес электронной почты нигде не отображается, необходим только для обратной связи.
Напрограммировано на Go 1.23.3, версия движка 2a6f89b