← Все статьи

SiteGuard12 мин чтения

Кракозябры вместо русского текста: как проверить кодировки сайта и MySQL

Вместо русского названия товара появились «РџСЂРёРІРµС‚», вопросительные знаки или символы замены. Проблема может находиться в файле, соединении с MySQL, самой записи или ответе браузеру. Поэтому запрос «кракозябры на сайте mysql utf8» не должен сразу приводить к массовому преобразованию таблиц: иногда данные целы и неверно показаны, а иногда ошибочный текст уже сохранён.

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

1. Выберите контрольную запись и сохраните исходное состояние

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

Установите границы проблемы. Повреждены все русские строки или только новые? Ошибка появилась после импорта, изменения подключения, редактирования шаблона или обновления компонента? Если меню читается, а названия из базы искажены, исследуйте различающийся путь получения текста. Если испорчены только записи одного источника, не распространяйте гипотезу на всю базу.

До изменения данных сделайте восстанавливаемую копию текущего состояния и сохраните исходный файл обмена отдельно. Такая копия нужна для возврата после неудачного исправления. Для восстановления уже утраченных букв потребуется другой источник — например, резервная копия до повреждения. Эти две задачи не следует смешивать.

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

2. Различайте неверное отображение, двойное преобразование и потерю

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

СостояниеЧто произошлоЧто проверять
Исходные байты целы, экран искажёнНеверно выбрана кодировка при чтенииHTTP-заголовок, метаданные и настройки клиента
Искажённые символы сохранены повторноНеверное прочтение превратилось в новые данныеТочный путь преобразований и возможность обратной операции
Вместо букв сохранены знаки вопросаСимволы могли быть заменены при преобразованииБайты записи и наличие первоначального источника
На экране появился символ �Декодер мог встретить недопустимую последовательностьВозникла замена только при показе или уже записана в данные

Если в контрольной строке вместо ожидаемых букв действительно сохранён байт 3F, это обычный знак вопроса. У сохранённого символа U+FFFD представление в UTF-8 — EF BF BD. По таким заменам нельзя однозначно восстановить исходные буквы. Но видимый символ � ещё не доказывает потерю в базе: браузер мог создать его при чтении неправильного ответа, сохранившиеся байты которого нужно исследовать отдельно.

3. Проверьте реальный HTTP-ответ и кодировку документа

В инструментах разработчика откройте Network и выберите запрос проблемного HTML-документа. Проверьте фактически полученный Content-Type, а не настройку, которую предполагалось отправить. Заголовок может изменить приложение, веб-сервер или промежуточный кеш. Для HTML в UTF-8 ожидается согласованное объявление:

Content-Type: text/html; charset=utf-8

В HTML обычно указывают соответствующую декларацию, расположенную в начале документа:

<meta charset="utf-8">

Алгоритм определения кодировки HTML учитывает BOM, сведения транспортного уровня и декларации внутри документа. При обычной загрузке без переопределения пользователем поддерживаемая кодировка HTTP рассматривается раньше поиска meta; BOM имеет более ранний приоритет. Поэтому добавление ещё одного meta не является универсальным исправлением ошибочного заголовка. Декларация кодировки должна целиком находиться в первых 1024 байтах.

Сравните выбранную браузером кодировку через document.characterSet с реальным содержимым. Просмотр Response уже может показывать декодированный текст. Для доказательства сохраните тело ответа без перекодирования и изучите нужный фрагмент в HEX; если ответ сжат, сначала корректно снимите транспортное сжатие. Не подменяйте байты копированием текста из уже искажённого DOM.

4. Отделите кодировку файлов от HTML-экранирования

Если неправильный текст находится прямо в шаблоне или языковом файле, проверьте его байты и способ сохранения редактором. Надпись «UTF-8» в настройках редактора не доказывает происхождение уже открытого файла. Перекодирование после ошибочного распознавания может сохранить неправильные символы в технически корректном UTF-8.

Автоматическое определение кодировки — гипотеза. Короткий фрагмент, состоящий из латинских букв и цифр, часто не позволяет различить несколько кодировок. Используйте известный русский образец и сведения о системе, которая создала файл. Исходный экземпляр сохраняйте неизменным, проверяя преобразование на копии.

htmlspecialchars экранирует специальные символы HTML; это не универсальный преобразователь Windows-1251 в UTF-8. Его параметр кодировки описывает входную строку. Если передать недопустимые для выбранной кодировки байты, поведение зависит от флагов; ENT_SUBSTITUTE может заменить их символом U+FFFD.

Поэтому добавление экранирования не ремонтирует уже испорченную кириллицу. Обратная ситуация тоже возможна: видимый текст &amp; вместо амперсанда связан с обработкой HTML-сущностей, а не обязательно с кодировкой MySQL. Сначала определите, какие именно символы находятся в строке до передачи шаблону.

5. Почему utf8mb4 нужен, но не исправляет любые кракозябры

В MySQL 8.4 название utf8 является устаревшим псевдонимом utf8mb3. Документация рекомендует utf8mb4 для новых приложений. Не закладывайте в настройки неопределённое предположение о том, что исторический псевдоним всегда будет означать одно и то же в будущих ветках: указывайте требуемый набор явно.

Разница состоит не в поддержке русского языка как такового. utf8mb4 поддерживает также дополнительные символы Unicode, для которых нужны четыре байта. У символов базовой многоязычной плоскости, поддерживаемых обоими вариантами, представление одинаково. Обычные русские буквы входят в эту область и нормально представляются в utf8mb3.

Следовательно, переход с utf8mb3 на utf8mb4 сам по себе не превращает уже сохранённое «Рџ…» обратно в исходный текст. Это разумное развитие схемы и совместимости, но отдельная задача от восстановления данных. Если проблема проявляется только на эмодзи, гипотеза о недостаточном наборе символов сильнее, чем при повреждении каждой русской буквы.

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

6. Проверьте конкретный столбец, а не только настройку базы

Кодировка определяет представление символов, а collation — правила сравнения и сортировки строк. Изменение collation способно повлиять на регистр, порядок выдачи и уникальность значений, но не является способом угадать исходные байты повреждённого текста.

У сервера, базы, таблицы и отдельных строковых столбцов могут различаться настройки. Получите описание именно поля контрольной записи: тип, набор символов, collation и связанные индексы. Общая надпись «база utf8mb4» не доказывает одинаковое состояние всех существующих столбцов.

Отдельно различайте изменение значения по умолчанию и преобразование данных. В описании ALTER TABLE смена табличного DEFAULT CHARACTER SET отделена от преобразования существующих строковых столбцов. Новый default используется при последующем добавлении столбцов без собственного указания кодировки; он не перекодирует всю старую информацию автоматически.

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

7. Измерьте кодировки действующего соединения

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

  • character_set_client — какой кодировкой сервер считает кодировку поступающих от клиента SQL-выражений.
  • character_set_connection — кодировка, в которую сервер преобразует поступившие выражения, с предусмотренными исключениями.
  • character_set_results — кодировка результатов, возвращаемых клиенту.
  • collation_connection — правила сравнения для соответствующих строковых выражений соединения.

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

SELECT @@session.character_set_client AS client_charset,
       @@session.character_set_connection AS connection_charset,
       @@session.character_set_results AS results_charset,
       @@session.collation_connection AS connection_collation;

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

Объявление кодировки должно соответствовать действительно переданным байтам. Указать utf8mb4 для строки Windows-1251 — не значит преобразовать её в UTF-8. Если настройка исправлена, новые записи могут начать сохраняться правильно, но старые повреждения останутся. Поэтому отдельно проверяют путь записи и существующие данные.

Иногда две ошибки частично компенсируют друг друга, и один интерфейс показывает читаемый текст при неверных настройках. Такой результат особенно опасно принимать за исправление: другой клиент или повторное сохранение может проявить повреждение. Сверяйте байты и ожидаемые символы независимо от внешнего вида одной панели. Настройку соединения и временные преобразования в коде оценивайте совместно, прежде чем убирать один из этих элементов.

8. Настройте подключение средствами драйвера PHP

Для PDO MySQL кодировку можно задать в параметре charset строки DSN. Учебный фрагмент ниже относится к созданию подключения приложения, которое уже использует UTF-8. Переменные с учётными данными предоставляются его штатной конфигурацией:

$pdo = new PDO(
    'mysql:host=db.internal;dbname=app;charset=utf8mb4',
    $dbUser,
    $dbPassword
);

Для mysqli предусмотрен метод set_charset, который документация предпочитает ручному запросу SET NAMES. Его вызывают после подключения и до работы с данными, проверяя результат либо исключение в принятом режиме обработки ошибок:

$mysqli->set_charset('utf8mb4');

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

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

9. Сравните байты на учебном примере «Привет»

Функции HEX, OCTET_LENGTH и CHAR_LENGTH позволяют сопоставить байты и длину строки. Для строкового аргумента HEX показывает по две шестнадцатеричные цифры на байт. OCTET_LENGTH считает байты, CHAR_LENGTH — кодовые точки. Ниже запрос к условной тестовой таблице; он не изменяет запись:

SELECT id, HEX(title) AS title_hex,
       OCTET_LENGTH(title) AS byte_count,
       CHAR_LENGTH(title) AS character_count
FROM encoding_probe
WHERE id = 42;
Представление контрольной строкиБайты в HEXДлина
«Привет» в UTF-8D09FD180D0B8D0B2D0B5D18212 байт, 6 символов
«Привет» в Windows-1251CFF0E8E2E5F26 байт, 6 символов при правильном прочтении
Шесть сохранённых знаков вопроса3F3F3F3F3F3F6 байт

Если UTF-8-байты первого варианта прочитать как Windows-1251, получится «РџСЂРёРІРµС‚». Сохранение этих ошибочных символов снова в UTF-8 даёт 25 байт и 12 кодовых точек. Это уже другой набор данных. Числа проверены для конкретного учебного слова; они не являются универсальным признаком повреждения любой строки.

Сравните HEX из базы, байты значения после чтения драйвером и байты HTTP-ответа. Совпадение первой пары и расхождение следующей локализует проблему ближе к формированию ответа. Если база уже содержит ошибочно сохранённый вариант, смена заголовка браузера не исправит запись.

Одна правильная строка не подтверждает состояние таблицы целиком. В контрольную выборку включите старую запись, новую запись из формы и объект из импорта. Для каждой сохраните источник и дату появления. Если дефект есть только у части данных, это основание разделить расследование по путям записи. Применять общий обратный преобразователь к смешанной выборке нельзя: корректный текст тоже может измениться.

10. Проследите импорт и дамп до первого неверного преобразования

При ошибке после импорта сохраните исходный файл и выясните его реальную кодировку у системы-источника. Затем проверьте декодирование в импортёре, параметры соединения и результат записи одного объекта. Формат CSV или SQL сам по себе не доказывает UTF-8. В одном файле могут встретиться данные, объединённые из разных источников.

У SQL-дампа изучите объявления кодировок и служебные SET NAMES, если они присутствуют. Параметры клиента импорта и команды внутри файла должны соответствовать его байтам. Простая замена надписи utf8 на utf8mb4 не перекодирует содержимое файла. Повторный импорт без проверки способен повторно преобразовать уже исправленные значения.

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

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

11. Выберите восстановление по доказанной причине

Если данные целы, исправьте ошибочный способ чтения или объявления кодировки. Если сохранено обратимое неправильное преобразование, разработчик сначала доказывает обратную операцию на копии и сверяет результат с известным источником. Для заменённых вопросами или U+FFFD букв нужен источник до потери информации; универсального алгоритма угадывания нет.

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

Переход схемы на utf8mb4 планируют отдельно: проверяют типы и размеры столбцов, индексы, уникальные значения при выбранной collation, внешние ключи и связанные таблицы. Преобразование может менять типы, требовать перестроения и влиять на доступность. Не отключайте проверки связей вслепую и не обещайте, что изменение пройдёт без блокировок на любой таблице.

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

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

12. Подтвердите полный цикл записи и чтения

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

  • Сопоставьте ожидаемый текст и HEX в базе для контрольных записей.
  • Проверьте русские буквы, «ё», кавычки и дополнительные символы Unicode.
  • Сверьте параметры соединения веб-приложения, импортёра и фоновых задач.
  • Подтвердите согласованность HTTP-charset, HTML-декларации и байтов ответа.
  • Проверьте поиск, сортировку и уникальность там, где менялась collation.

Убедитесь, что видите свежий ответ, а не старую страницу из кеша. Итоговая запись должна называть первый ошибочный этап, затронутые данные и выполненную проверку. Формулировка «везде поставили UTF-8» не объясняет ни причину, ни возможность повторения сбоя.

Подключите SiteGuard для внешнего контроля HTTP-доступности сайта после изменений. Кодировки и сохранность текста проверяет ваша команда.