Skip to content

Решаем проблему с кривой кодировкой на сайте

Решаем проблему с кривой кодировкой на сайте

Для начала определяем, в какой кодировке должны отображаться данные на сайте.

Для этого смотрим на сайт, ищем там строку вида

<meta charset="какое-то значение" />
если часть строк на сайте отображается корректно(строки, защитые в шаблон или файлы локализации скрипта), а часть строк, взятая из базы – отображается криво, дополнительно проверяем в какой кодировке отображаются корректно отображающиеся строки, и в какой – данные из БД. Для этого удобно пользоваться плагином "Set Character Encoding" из Google Chrome Web Store. Смотрим в какой кодировке отображается сайт сейчас, пробуем другие кодировки, к примеру для не корректно отображающегося текста с кириллическими символами пробуем Windows-1251, Windows-1252(бывает что в базу данные писали в latin1, а читают как utf8), koi8-r и т.п. Если на какой-то кодировке строки, отображавшиеся нормально начинают отображаться не корректно, зато строки, полученные из БД выглядят корректно – значит мы нашли кодировку, в которой хранятся данные в базе.

Также по виду кракозябр и блок-схеме из https://habr.com/ru/post/147843/ можно понять, в какой кодировке хранятся в базе данные, и в какой читаются из неё.

Определили кодировки? Теперь ищем виноватых.

Очень часто это БД mysql. Данные слили без указания кодировки в дамп на одном сервере, где дефолтной указана одна кодировка(к примеру utf8), создали базу в дефолтной кодировке на другом сервере(где дефолтной кодировкой является latin1), импортировали utf8 как latin1 и получили данные в кривой кодировке.

Смотрим, что у нас в базе, подлкючившись к ней консолью mysql.

К примеру, в нашем случае мы видим

mysql> SELECT text FROM test_table limit 1;
+---------------------------------------+
| text                                  |
+---------------------------------------+
| ТеÑÑ‚Ð¾Ð²Ð°Ñ Ñтрока            |
+---------------------------------------+
1 row in set (0.00 sec)

Это похоже на UTF-8, записанную как latin1.

Похожие кракозябры можно получить командой

echo 'Тестовая строка' | iconv  -c -f windows-1252 -t utf8

Делаем

mysql> set names latin1;
Query OK, 0 rows affected (0.00 sec)

повторяем

mysql> SELECT text FROM test_table limit 1;
+-----------------+
| text            |
+-----------------+
| Тестовая строка |
+-----------------+
1 row in set (0.00 sec)

и видим, что наша догадка верна.

Смотрим в конфигурационный файл своей CMS, видим там что-то(к примеру, у нас WP) вроде

define( 'DB_CHARSET', 'utf8' );

В нашем случае есть два варианта решения проблемы.

Грязный хак для быстрого фикса проблемы с кодировкой

Первый – грязный хак, но требующий минимум приложеннных усилий. Просто значение DB_CHARSET с utf8 на latin1. Сайт отобразится нормально. Но, этот подход чреват проблемами в будущем. Если мы используем расширенные возможности MySQL для сравнения строк и поиска(какой-то плагин CMS используе) – часть запросов может работать не корректнов. А главное, при последующем переносе сайта на другой хост могут возникнуть проблемы.

Красивое решение

Сохраняем дамп БД сайта с --skip-set-charset и принудительным указанием кодировки --default-character-set=latin1. Мы же помним, что БД у нас хранит данные, конвертированные по схеме utf-8⇾cp1251(или latin1)? Чтобы данные выгрузились в чистом utf-8 и нормально читались как utf-8 нам необходимо выгрузить их как latin1, а затем импортировать как utf8.

Делаем:

mysqldump --skip-set-charset --default-character-set=latin1 -uour_user -p our_db > some_dump.sql

Определяем, какие collation указаны в дампе для данных, записанных ранее как latin1.

grep -oP 'latin1_bin|latin1_[a-z]{1,10}_c[i,s]' some_dump.sql

Полученную строку(к примеру latin1_swedish_ci), или строки, используем для замены вида

sed -i 's/latin1_swedish_ci/utf8_general_ci/g' some_dump.sql

Также меняем кодировку

sed -i 's/latin1/utf8/g' some_dump.sql

Или заменяем эти вхождения на нужные вам(в нашем случае latin1 на utf8 и latin1_swedish_ci на utf8_general_ci) руками.

Импортируем дамп уже в корректном виде в верной кодировке

mysql  --default-character-set=utf8 -uour_user -p our_db < some_dump.sql

Проблема решена, сайт отображается в utf8, и кириллические строки выглядят как "Тестовая строка", а не кракозябры вида "ТеÑÑ‚Ð¾Ð²Ð°Ñ Ñтрока".

Когда в скриптах не указана кодировка подключения к mysql

Если использовалась системная кодировка подключения(не была указана кодировка подключения в конфигурационном файле сайта), указываем нужную кодировку.

Если сайт самописный, и его конфигурационный файл не подразумевает возможности указания кодировки подключения, делаем в домашнем каталоге сайта

grep -P -l -r 'mysqli?_connect|new mysqli'
чтобы получить список файлов, где используются функции mysql_connect или mysqli_connect, или метод mysqli construct.

Если в скриптах используется mysqli

$conn = new mysqli($servername, $username, $password,$dbname);
указываем

$conn -> set_charset("utf8");

где utf8 – нужная нам кодировка подключения.

Если скрипт написан в процедурном стиле, то после строки вида

$conn = mysqli_connect($servername, $username, $password, $dbname);
необходимо указать

mysqli_set_charset($conn,'utf8')  

Минуточку вашего драгоценного внимания!

Не пытайтесь использовать с mysqli старый подход с mysqli_query("SET NAMES 'utf8'") – он не сработает. Вызов mysqli_set_charset в случае с mysqli является единственным корректным способом указать кодировку подключения.

Если скрипт написан с использованием модуля mysql

После строки

$conn = mysqli_connect($servername, $username, $password, $dbname);

указываем

mysql_set_charset('utf8',$db_conn);

или

mysql_query("SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'");
и, возможно, если потребуется

mysql_query("SET CHARACTER SET 'utf8'");
mysql_query("SET character_set_results='utf8'");
При этом вариант с mysql_set_charset предпочтительней варианта с mysql_query, и я рекомендую использовать именно его.

Что делать, когда записи в одной таблице базы данных указаны в разных кодировках

Есть часть записей в таблицу была записана в одной кодировке, а часть(к примеру после переноса) – уже в другой кодировке, в этом случае необходимо конвертировать записи в кодировке, отличающейся от нормальной, в нужную нам.

Делается это с помощью запросов update с использованием convert.

Полезная инфа

https://nicj.net/mysql-converting-an-incorrect-latin1-column-to-utf8/ https://mysqlserverteam.com/debugging-character-set-issues-by-example/