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

Меня зовут Андрей Зенков, я руковожу веб-студией «Мельница» с 2013 года. За это время я разбирал сотни ситуаций, где текстовые данные оказывались нечитаемыми из-за банальной путаницы с кодировками. В этой статье объясню, как компьютер хранит текст, почему существует Windows-1251, как связаны ascii и unicode, и что делать, когда кракозябры снова появятся на экране. Всё это встречается в повседневной разработке - в HTML-разметке, в настройках серверов, в операционных системах.

Кодировка - это таблица соответствий: какой числовой код называется каким символом. Браузер определяет кодировку страницы по заголовку от сервера или по мета-тегу charset в HTML - если информация неверная или отсутствует, вместо текста появляется бессмыслица.

Пройдём путь от азов до практики: как устроен ASCII, зачем появилась Windows-1251, что такое Unicode и когда лучше выбирать UTF-8. Углублённое программирование оставим в стороне - только то, что реально нужно разработчику и владельцу сайта.

Как компьютер хранит текст - биты, байты и таблица символов

Главное, что нужно понять: компьютер не знает ни букв, ни картинок, ни звуков. Он работает только с числами - точнее, с последовательностями нулей и единиц. Любые данные, которые хранятся на диске или передаются по сети, существуют в бинарном формате. Это фундамент, без которого кодировки не имеют смысла.

Минимальная единица информации - бит. Один бит принимает два значения: 0 или 1. Один байт состоит из восьми бит. С помощью одного байта можно закодировать 256 различных значений - от 0 до 255 (это 2^8). Именно поэтому один байт долгое время был стандартной единицей для хранения одного символа текста.

Теперь возникает вопрос: как компьютер понимает, что число 65 - это буква «A», а не просто число 65? Ответ - таблица символов. Это справочник, где каждому числовому коду сопоставлен конкретный символ. Например, в большинстве стандартных таблиц число 65 соответствует заглавной латинской «A», а 97 - строчной «a». Без согласованной таблицы символов одна программа запишет в файл число 65 как «A», а другая прочитает его как что-то совершенно иное.

Для компактной записи кодов символов часто используют шестнадцатеричную систему счисления. В десятичной системе у нас цифры 0-9. В шестнадцатеричной добавляются буквы A-F, чтобы представить значения от 10 до 15 одним знаком. Число 65 в шестнадцатеричном (hex) формате записывается как 41. Число 255 - как FF. Это стандартный способ представления байтов в коде, в дампах памяти, в hex-редакторах.

Покажу на конкретном примере из практики. Когда я открываю дамп базы данных в hex-редакторе, вижу именно шестнадцатеричные коды символов. Для text-файла в кодировке UTF-8 русская буква «А» (заглавная) хранится как два байта: D0 90. В Windows-1251 та же буква «А» - это один байт: C0. В обоих случаях компьютер хранит числа, а не букву. Вот почему при смене кодировки текст рассыпается: программа читает числа через «не ту» таблицу символов и показывает не те значения символов.

Запомните главное: кодировка - это и есть таблица соответствий между числами и символами. Компьютер хранит числа. Программа должна знать, через какую таблицу их читать. Если таблица не совпадает с той, через которую данные записывались - текст может быть закодирован правильно, но отображаться как полная бессмыслица. Вот и вся механика кракозябр.

ASCII - american standard code for information interchange

В 1963 году в США появился стандарт, который на десятилетия определил развитие компьютерной индустрии. Его создали для передачи текстовых данных через телетайпы - устройства, которые печатали сообщения по телефонным и телеграфным линиям. Стандарт назвали ASCII, и произносится он как «аски».

Аббревиатура расшифровывается так: American Standard Code for Information Interchange - «американский стандартный код для обмена информацией». Один из авторов стандарта - Боб Бемер, инженер IBM, который работал над унификацией компьютерных систем того времени. До появления единого standard code for information exchange разные компании использовали несовместимые кодировки, и передача текста между устройствами была настоящей головной болью.

ASCII содержит всего 128 символов - от 0 до 127. Они делятся на две группы:

  • Управляющие символы (0-31 и 127): непечатные коды, которые управляют поведением устройства. Код 10 - перевод строки (Line Feed), код 13 - возврат каретки (Carriage Return), код 7 - звуковой сигнал (Bell). Разработчики встречают их постоянно, особенно при работе с файлами из разных операционных систем.
  • Печатные символы (32-126): пробел, цифры от 0 до 9, буквы латиницы (прописные и строчные), набор знаков пунктуации и специальных символов - @, #, $, %, & и другие.

Важная деталь в устройстве таблицы: латинские буквы расположены в строгом алфавитном порядке. Заглавная «A» имеет код 65, «B» - 66, «C» - 67 и так далее до «Z» с кодом 90. Строчные буквы идут следом: «a» - код 97, «b» - 98. Это не случайность - такой порядок упрощает сортировку текста. Достаточно сравнивать числовые коды, и слова выстроятся в алфавитном порядке автоматически, без дополнительной логики в программе.

В 1986 году ascii был принят организацией ANSI как официальный american standard - стандарт ANSI X3.4-1986. С тех пор он стал стандартом для большинства современных кодировок: первые 128 символов в UTF-8 полностью совпадают с ASCII. Это обратная совместимость, которая сохраняется по сей день. Именно поэтому сайты на чисто английском тексте работали без проблем при любой кодировке, а сложности начинались ровно тогда, когда нужна была кириллица.

Но у ASCII есть очевидный недостаток, который и привёл к появлению множества национальных кодировок. 128 символов хватает для английского языка, однако в таблице нет ни кириллицы, ни немецких умляутов, ни французских диакритических знаков. Каждая страна начала создавать собственное расширение: Германия - своё, Франция - своё, СССР и Россия - своё. Так появился целый зоопарк несовместимых стандартов, и ASCII превратился из универсального решения в фундамент, на котором каждый строил что-то своё. Именно из этого хаоса и выросла Windows-1251 - о ней поговорим в следующем разделе.

Кодировка Windows-1251 - таблица и структура

Windows-1251 появилась в 1990-1991 году - её создали совместно компании Параграф, Диалог и Microsoft Russia специально для русских версий Windows. С тех пор она стала стандартной кодировкой в операционных системах Windows вплоть до версии 10, где Microsoft окончательно перешла на Unicode.

По устройству windows 1251 - классическая 8-битная кодировка. Один байт кодирует один символ, итого 256 позиций. Первые 128 символов (диапазон 0x00-0x7F в шестнадцатеричной записи) полностью совпадают с ASCII - латиница, цифры, знаки препинания. Это сделано намеренно: файлы с чисто английским текстом совместимы с обеими системами без конвертации.

Вторые 128 позиций (диапазон 0x80-0xFF) - набор расширенных символов, уникальный для кодировки Windows. Здесь расположены кириллические буквы в алфавитном порядке: заглавные А-Я начиная с кода 0xC0, строчные а-я с кода 0xE0. Плюс несколько дополнительных позиций: №, знаки валют, типографские кавычки и тире.

Именно здесь cp1251 принципиально отличается от своей предшественницы CP866, разработанной для DOS-терминалов. В CP866 часть таблицы символов была занята псевдографикой - символами для рисования рамок в текстовом режиме. Windows-1251 от псевдографики отказалась и поставила на её место типографские символы. KOI8-R - альтернативная кириллица для Unix-систем и email - размещает русские буквы по принципу звучания: А там, где у латиницы A, Б там, где B. ISO-8859-5 тоже содержит символы кириллицы, но со сдвигом ровно на 16 байт относительно Windows-1251. Windows-1251 стала доминировать именно потому, что была стандартом для массовых настольных компьютеров на Windows - а их в 1990-е было большинство.

Ниже - таблица символов Windows-1251 для диапазона 0x80-0xFF. Ключевые позиции, которые реально нужны при работе с кириллицей в кодировке Windows:

DEC HEX Символ Описание
128 80 Ђ Сербская буква Дже
129 81 Ѓ Македонская буква Гже
168 A8 Ё Заглавная Ё
184 B8 ё Строчная ё
185 B9 Знак номера
186 BA є Украинская строчная є
192 C0 А Заглавная А - начало кириллицы в алфавитном порядке
193 C1 Б Заглавная Б
194 C2 В Заглавная В
195 C3 Г Заглавная Г
196 C4 Д Заглавная Д
200 C8 З Заглавная З
207 CF П Заглавная П
215 D7 Х Заглавная Х
220 DC Ь Заглавная Ь
223 DF Я Заглавная Я - конец заглавных букв
224 E0 а Строчная а - начало строчных
225 E1 б Строчная б
239 EF п Строчная п
240 F0 р Строчная р
255 FF я Строчная я - конец кириллического блока

Такая структура объясняет практическое удобство кодировки: кириллические символы расположены строго в алфавитном порядке, поэтому байтовое сравнение строк сразу даёт правильную сортировку. Если ваш сайт работал на Windows-сервере и использовал стандартную кодировку Windows по умолчанию, база данных и все текстовые файлы, скорее всего, тоже были в Windows-1251. Это важно понимать при миграции на Linux-хостинг или переходе на UTF-8.

Unicode и UTF-8 - зачем понадобилась универсальная кодировка

Программист работает с Unicode и таблицей символов UTF-8 на компьютере

К концу 1980-х хаос с кодировками достиг предела. Десятки несовместимых стандартов по всему миру: для японского - Shift-JIS и EUC-JP, для арабского - ISO-8859-6, для кириллицы - KOI8-R, CP866, Windows-1251 и ещё несколько. Передать документ между системами без потерь было настоящей проблемой. Это и стало причиной создания универсальной кодировки, которую поддерживают все без исключения.

В 1991 году появился unicode - единый стандарт, который поддерживают все современные платформы: операционные системы, браузеры, языки программирования. Его задача - охватить все языки мира в одной таблице. Сейчас в Unicode больше миллиона кодовых позиций (точнее - 1 114 112), хотя реально занято из них около 150 тысяч. Здесь есть всё: латиница, кириллица, иероглифы, арабский, эмодзи, исторические письменности.

Важно понимать: unicode - это только таблица «символ - номер». А как этот номер записывается байтами - определяет конкретная utf-кодировка. Их несколько:

  • UTF-8 - переменная длина от 1 до 4 байт на символ. Латиницу и ASCII кодирует одним байтом, кириллица занимает 2 байта на каждый символ, редкие иероглифы - 3-4 байта. Именно это сделало UTF-8 стандартом веба: английский текст весит столько же, сколько в ASCII.
  • UTF-16 - 2 байта на большинство символов, кодирует до 65 536 символов в базовой плоскости. Используется внутри Windows и в Java.
  • UTF-32 - всегда 4 байта на символ, файлы в 4 раза тяжелее UTF-8 для обычного текста. Применяется редко, в основном для внутренней обработки.

Теперь о том, что реально создаёт проблемы - BOM. Byte Order Mark - это специальные сигнатуры в начале файла (EF BB BF для UTF-8), которые сообщают программе: «перед тобой UTF-8». Звучит разумно. На практике - источник боли. php интерпретирует BOM как текст и выводит его перед DOCTYPE. Браузер видит этот мусор до тега Content-Type - и в итоге браузер покажет ошибку вместо нормальной страницы. Результат: сломанные HTTP-заголовки, кривое отображение, иногда полный отказ скрипта.

Моя рекомендация: всегда при сохранении файла выбирать UTF-8 без BOM. Большинство современных редакторов поддерживают оба варианта - просто нужно выбрать правильный. Проверить просто: открой файл в hex-редакторе и посмотри первые три байта. Если видишь EF BB BF - это BOM, его нужно убрать.

Главное практическое следствие: когда сервер отдаёт страницу, он указывает в HTTP-заголовке кодировку файла. Браузер открывает файл в той кодировке, которую получил от сервера. Если они не совпадают - вместо текста увидите кракозябры. Об этом подробно - в следующем разделе.

Кракозябры - почему возникают и как исправить

Кракозябры - это всегда одно и то же: браузер или программа читают байты файла не в той кодировке, в которой они записаны. Вот как это происходит. Браузер определяет кодировку по заголовку от сервера в HTTP-ответе или по мета-тегу charset в html-документе. Открыть файл в той кодировке, которую ему передал сервер - именно так и работает браузер. Если информация, которую ему передал сервер в заголовке, неверна или мета-тег отсутствует - браузер угадывает. И угадывает не всегда правильно. В каком месте не выдерживает эта цепочка соответствий - там и появляются кракозябры.

На практике я встречал три типичных сценария:

  1. Браузер показывает кракозябры на страницах сайта. Сервер отдаёт неверный charset или мета-тег отсутствует в html. Браузер пытается угадать кодировку самостоятельно.
  2. Текстовый редактор не может корректно открыть файл. Выбрана не та кодировка при открытии. Файл физически нормальный, просто программа читает его неверно.
  3. PHP или база данных возвращают кракозябры. Соединение с MySQL открыто без SET NAMES - данные проходят через неправильное преобразование.

Теперь конкретное исправление для каждого случая.

HTML. В блок <head> добавьте первой строкой:

<meta charset="UTF-8">

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

PHP. До любого вывода - до echo, до пробела - добавьте:

Строка для вставки: header('Content-Type: text/html; charset=utf-8');

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

Notepad++ (это тот самый блокнот для разработчиков, который стоит у каждого в закладках). Чтобы открыть файл и проверить его кодировку: перейди во вкладку Encoding в верхнем меню. Там кодировку файла он показывает текущей галочкой - файл отображается именно в той кодировке, которую считал редактор. Если надо перекодировать - нажать «Convert to UTF-8» и затем сохранить файл через Ctrl+S. Инструменты конвертации в Notepad++ не меняют файл на диске автоматически - только после сохранить.

.htaccess. Если управляешь сервером Apache, добавь в корневой .htaccess:

AddDefaultCharset utf-8

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

MySQL. Сразу после открытия соединения:

SET NAMES 'utf8mb4';

Важно: используй именно utf8mb4, а не просто utf8 - в MySQL utf8 это урезанная кодировка без поддержки 4-байтовых символов (эмодзи и часть редких знаков). Или используй инструменты уровня PDO - там charset прописывается прямо в строке DSN при подключении.

Расскажу случай из практики - разбора вживую лучше теории. Несколько лет назад обратился клиент с интернет-магазином на самописной CMS. Кракозябры только в описаниях товаров - всё остальное на страницах вижу нормально, заголовки, меню, блоки - всё корректно. Сайт в UTF-8, база в UTF-8, html объявление правильное. Час разбирались. Оказалось: менеджер загружал описания через старый импорт-скрипт, который открывал соединение с базой без SET NAMES. Данные писались в таблицу с неверным преобразованием. Выгрузили, перекодировали через iconv, залили обратно - и никаких глюков все работает именно так, как должно. Типичный пример: цепочка почти корректно настроена, но одно звено пропущено.

Главное правило: если всё настроено корректно на каждом уровне - файл, сервер, html-объявление, соединение с базой - глюков все работает именно без сюрпризов. Кракозябры всегда указывают на конкретное звено, где кодировки разошлись. Найдите это звено - и исправление будет простым.

Windows-1251 или UTF-8 - что выбрать

Веб-разработчик настраивает кодировку сайта в редакторе кода

Меня часто спрашивают: какую кодировку выбрать для нового проекта? Ответ простой - UTF-8 без BOM. Всегда. Для новых проектов это единственный разумный выбор, и объясню почему.

UTF-8 сегодня используется на более чем 97% сайтов в интернете. Все современные браузеры, фреймворки, библиотеки поддерживают её по умолчанию. PHP начиная с версии 5.6 ориентирован на UTF-8 (а сейчас актуален PHP 8.x, где это уже давно умолчание). Если вы создаёте API, мобильное приложение, мультиязычный сайт - UTF-8 это стандарт, а не вопрос предпочтений. Любой другой выбор создаст головную боль при интеграции с внешними сервисами, которые ожидают именно его.

Но есть ситуации, когда Windows-1251 вообще не ошибка, а вынужденная реальность. Речь про legacy-системы: старые базы данных, государственные сервисы с фиксированным форматом обмена, корпоративные системы 2000-х годов, старый импорт-скрипт, которого никто не трогал 10 лет. Если проект на Windows-1251 работает нормально без сбоев - не трогайте. Сначала посмотреть на последствия миграции, потом принять решение. Это самый распространенный совет, который я даю клиентам: работает - не чини.

Теперь про момент, который снимает половину тревог. Если браузер получает файл с правильно объявленной кодировкой в HTML-заголовке и на сервере - тоже похер на кодировку файла в каком-то абстрактном смысле. Неважно, Windows-1251 или UTF-8 - если объявлено корректно везде, никаких глюков все работает. Проблемы начинаются именно при смешении: файл в одной кодировке, объявление в другой, база в третьей. Вот тогда кракозябры и вылезают.

Отдельный момент про разработку на локальном компьютере. Кодировка локального компьютера должна быть согласована с кодировкой сервера. Если редактор на вашей машине сохраняет файл в Windows-1251, а сервер ожидает UTF-8 - при загрузке получите проблемы. Поэтому настройте редактор один раз: UTF-8 без BOM для сохранения файлов, и забудьте об этом навсегда.

Что делать, если нужно преобразовать существующий проект с Windows-1251 на UTF-8? Здесь главное - не делать это частично. Подходит только полная миграция: конвертируете все файлы, конвертируете базу данных, меняете объявления в HTML и PHP-конфигурации, проверяете настройки сервера. Если сделать только часть - например, конвертировать только файлы, но забыть про базу - получите ровно тот же результат, что и до миграции, только с другими кракозябрами.

Практический итог от меня лично: новый проект - сразу UTF-8 без BOM, это решается один раз в настройках редактора и сервера. Существующий проект на Windows-1251 работает - оставьте как есть, не создавайте риски. Мигрируете - делайте всё и сразу, проверяйте каждый уровень. Половина кракозябр в моей практике появлялась именно из-за частичной конвертации, когда программист файлы конвертировал, а про базу данных забыл.

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

Список литературы

  1. Консорциум Unicode. The Unicode Standard.
  2. Международная организация по стандартизации. ISO/IEC 8859-5: Information technology - Character sets. ISO.
  3. Яндекс. Кодировка сайта. Яндекс.Помощь для вебмастеров.
  4. Кодировка текста ASCII, Windows-1251, CP866, KOI8-R и Юникод UTF-8, 16, 32 - как исправить проблему кракозябров. JavaRush, 2018.
Поделитесь Вашим мнением
  1. Рустем

    Спасибо за статью

Ваш комментарий

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


Еще записи из этой же рубрики

Что будем искать? Например,Хостинг

Минуту внимания
Мы используем файлы cookies, чтобы обеспечивать правильную работу нашего веб-сайта, а также работу функций социальных сетей и анализа сетевого трафика.