Перейти к содержимому

Кракозябры вместо текста - кодировки файлов, базы и обмена

Ищем, где ломается кодировка: файлы шаблона, база и соединение, импорт из файла, письма и выгрузки, и приводим проект к одной кодировке.

Что нужно знать заранее

У проекта одна кодировка на всё: файлы, база, соединение с базой и ответ браузеру. Кракозябры появляются там, где одно из звеньев цепочки выбивается из общего правила.

Современные проекты живут в универсальной кодировке, а старые - в однобайтовой. Смешивать их в одном проекте нельзя: перекодировка на каждом шаге рано или поздно теряет часть символов текста.

Чинить кодировку надо у источника данных, а не в месте их показа. Замена символов при выводе выглядит как решение ровно до первого поиска или сравнения строк в базе.

Шаги

  1. Узнать кодировку сайта из настроек языка и сверить её с кодировкой файлов.
  2. Проверить кодировку таблиц базы и кодировку соединения с ней прямо из кода.
  3. Определить кодировку входящего файла до импорта и перекодировать его при чтении.
  4. Проверить кодировку писем и выгрузок отдельно: у них свои собственные настройки.
  5. Привести все звенья к одной кодировке и повторить проверку на копии.

Решение

Смотрим кодировку сайта и файлов:

Окно терминала
grep -rn 'CHARSET' /home/bitrix/www/bitrix/php_interface/dbconn.php | head
file -I /home/bitrix/www/local/templates/main/header.php # ожидаем charset=utf-8
# файл шаблона в другой кодировке ломает всю страницу целиком

Кодировка сайта задана в настройках языка, а файлы обязаны ей соответствовать. Один файл, сохранённый чужим редактором, портит страницу целиком, и обычно это именно недавно правленый файл.

Проверяем базу и соединение:

SELECT DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = DATABASE();
SHOW VARIABLES LIKE 'character_set_%'; -- кодировка соединения и клиента
-- расхождение кодировок таблиц и соединения даёт кракозябры при выборке

Кодировка соединения важна не меньше кодировки таблиц. Данные могут лежать правильно, а ломаться уже при чтении, и это самый неприятный случай: в базе всё хорошо, а на витрине мусор.

Определяем кодировку входящего файла:

$content = file_get_contents($path);
$encoding = mb_detect_encoding($content, ['UTF-8', 'Windows-1251'], true);
printf("файл в кодировке %s\n", $encoding ?: 'не определена');
// прайс поставщика приходит в однобайтовой кодировке чаще, чем в универсальной

Перекодируем при чтении, а не после записи:

if ($encoding && $encoding !== 'UTF-8') {
$content = mb_convert_encoding($content, 'UTF-8', $encoding); // один раз, на входе
}
// перекодировка уже сохранённых данных - отдельная операция с резервной копией

Перекодировка на входе - единственный дешёвый вариант. Данные, записанные в неверной кодировке, чинятся уже миграцией с копией базы, а не одной строчкой в шаблоне.

Проверяем выгрузку в таблицу:

$out = mb_convert_encoding($csv, 'Windows-1251', 'UTF-8'); // для старых версий Excel
file_put_contents($file, $out);
// либо оставляют универсальную кодировку и добавляют метку порядка байтов

Выгрузка для таблиц - отдельная история со своими правилами. Старые версии табличных редакторов открывают файл в системной кодировке, поэтому его либо перекодируют, либо помечают специальной меткой в начале файла.

Типичные проблемы

Вся страница превратилась в мусор после правки шаблона.

Файл сохранён редактором разработчика в другой, не принятой на проекте кодировке. Кодировку файла проверяют командой определения типа и пересохраняют его правильно.

В базе данные верные, а на витрине кракозябры.

Кодировка соединения с базой отличается от кодировки самих таблиц проекта. Настройку соединения проверяют системными переменными на самом сервере базы данных.

После импорта прайса названия товаров испорчены.

Файл поставщика пришёл в однобайтовой кодировке, а прочитан кодом как универсальный. Кодировку определяют автоматически и перекодируют содержимое прямо при чтении файла.

Выгрузка открывается в таблице нечитаемой.

Табличный редактор открывает такой файл в системной кодировке операционной системы. Файл перекодируют под этот редактор либо добавляют в начало метку порядка байтов.

Часть символов пропала после ремонта кодировки.

Данные перекодировали дважды либо исходили из неверно определённой исходной кодировки. Перед такой правкой снимают копию базы и проверяют результат на ней.

Частые вопросы

Как понять, в какой кодировке работает сайт?

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

Можно ли перевести старый сайт в универсальную кодировку?

Можно, но это отдельный проект: файлы, база, обмены и выгрузки переводят вместе. На копии такой перевод проверяют полностью, включая обмен с учётной системой.

Почему кракозябры только в письмах?

У писем своя кодировка и свои заголовки, задаваемые почтовым событием. Проверяют шаблон письма и настройки почтовых событий отдельно от сайта.

Помогает ли замена символов при выводе?

Нет, это маскировка: поиск и сравнение строк всё равно работают с испорченными данными. Чинят источник, а не место показа.

Что делать с файлом неизвестной кодировки?

Определить её автоматически по содержимому и перекодировать при чтении. Если определение не сработало, кодировку уточняют у отправителя файла.

Смежное

Первоисточник