Решаем проблему с кривой кодировкой на сайте
Решаем проблему с кривой кодировкой на сайте¶
Для начала определяем, в какой кодировке должны отображаться данные на сайте.
Для этого смотрим на сайт, ищем там строку вида
<meta charset="какое-то значение" />
Также по виду кракозябр и блок-схеме из 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'
Если в скриптах используется 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'");
Что делать, когда записи в одной таблице базы данных указаны в разных кодировках¶
Есть часть записей в таблицу была записана в одной кодировке, а часть(к примеру после переноса) – уже в другой кодировке, в этом случае необходимо конвертировать записи в кодировке, отличающейся от нормальной, в нужную нам.
Делается это с помощью запросов update с использованием convert.
Полезная инфа¶
https://nicj.net/mysql-converting-an-incorrect-latin1-column-to-utf8/ https://mysqlserverteam.com/debugging-character-set-issues-by-example/