Кодировка Windows-1251: как хранится кириллица
Кодировка Windows-1251 - это однобайтовая восьмибитная таблица символов, разработанная Microsoft в начале 1990-х годов специально для представления кириллических букв в операционных системах Windows. На протяжении двух десятилетий она была стандартом де-факто для русскоязычных сайтов, офисных документов и системных сообщений. Несмотря на то что сегодня её постепенно вытесняет Unicode в кодировке UTF-8, Windows-1251 по-прежнему встречается в устаревших базах данных, файловых архивах и конфигурационных файлах - и знать её устройство необходимо любому, кто работает с текстом на уровне байтов. Чтобы сразу увидеть, как каждый символ превращается в последовательность битов, воспользуйтесь калькулятором ниже: он показывает побитовую карту и распределение байт по зонам таблицы.
Структура таблицы Windows-1251
Вся таблица Windows-1251 вмещает ровно 256 символов - по одному на каждое из 256 значений восьмибитного байта (). Первые 128 позиций (0x00–0x7F, десятичные 0–127) полностью совпадают с таблицей ASCII: цифры 0–9 занимают коды 48–57, заглавные латинские буквы - 65–90, строчные - 97–122. Это означает, что любой ASCII-текст (английский, цифры, знаки препинания) при хранении в Windows-1251 занимает ровно столько же байт и выглядит идентично.
Вторые 128 позиций (0x80–0xFF, десятичные 128–255) - расширенная зона, именно здесь расположена кириллица. Ключевой диапазон:
| Символ | Hex | Десятичный |
|---|---|---|
| А (заглавная) | 0xC0 | 192 |
| Б | 0xC1 | 193 |
| … | … | … |
| Я | 0xDF | 223 |
| а (строчная) | 0xE0 | 224 |
| б | 0xE1 | 225 |
| … | … | … |
| я | 0xFF | 255 |
| Ё | 0xA8 | 168 |
| ё | 0xB8 | 184 |
Заглавные буквы А–Я идут подряд в диапазоне 0xC0–0xDF (192–223), строчные а–я - в 0xE0–0xFF (224–255). Буква «ё»/«Ё» стоит отдельно: 0xA8 и 0xB8 - это историческое решение, вызывавшее немало проблем при сортировке. Для перехода от кода строчной буквы к коду заглавной достаточно вычесть 32: . Такая же разница существует между латинскими строчными и заглавными в ASCII.
Как символ превращается в байт
Когда программа сохраняет кириллический текст в Windows-1251, каждый символ заменяется одним числом из таблицы. Слово «Мир» записывается тремя байтами:
В двоичном виде буква «М» (0xCC) выглядит так: . Старший бит равен 1 - это признак расширенной зоны (коды 128–255). У всех кириллических символов, начиная с 0xC0, старший бит всегда единица, что и отличает их от ASCII-символов, у которых старший бит равен нулю.

Именно восьмой бит (значение 128) «включает» расширенную зону. Это объясняет, почему семибитный ASCII вмещает только 128 символов и для кириллицы не хватает места: нужен восьмой бит, а он уже занят.
Кракозябры: откуда берётся кодировочная путаница
«Кракозябры» - неформальный термин для нечитаемого текста, возникающего при несоответствии кодировок: файл был создан в одной, а открыт в другой. Рассмотрим типичный сценарий.
Буква «П» в Windows-1251 имеет код 0xCF (207). Если тот же байт 0xCF открыть, предполагая кодировку UTF-8, интерпретатор обнаружит, что байт с битом 11 в начале означает начало двухбайтовой последовательности, а следующий байт не является продолжением (не начинается с 10). Это ошибка декодирования, и браузер или текстовый редактор вместо «П» покажет знак замены - обычно символ или вопросительный знак в квадрате.
При открытии файла Windows-1251 в кодировке ISO-8859-1 (Latin-1) байт 0xCF попадает в область символа Ï (буква с умляутом) - именно такие латинские символы видят пользователи вместо русского текста.
Главное правило: кодировка - это соглашение, а не свойство самого байта. Байт 0xCF означает «П» только при условии, что обе стороны (запись и чтение) используют Windows-1251. Без этого контракта последовательность байт бессмысленна.
Наиболее распространённые причины кракозябр:
- Файл .html без тега
<meta charset="windows-1251">или с неверным значением charset. - База данных MySQL с полем
latin1, в которое записан Windows-1251 текст. - Текстовый файл, сохранённый в Notepad в «ANSI» (Windows-1251 для русской Windows), открытый в Linux-редакторе с дефолтной кодировкой UTF-8.
- Письмо электронной почты с заголовком
Content-Type: text/plain; charset=utf-8, тело которого фактически закодировано в Windows-1251.
Windows-1251 против UTF-8: сколько байт на символ
Принципиальное отличие Windows-1251 от UTF-8 - способ выделения байт под символы. Windows-1251 фиксировала по одному байту на любой символ из своей таблицы, что делало расчёт длины строки в байтах тривиальным: длина в байтах всегда равна количеству символов.
UTF-8 использует переменное число байт: от одного (ASCII) до четырёх. Кириллические буквы в Unicode занимают диапазон U+0410–U+044F, и UTF-8 кодирует их двумя байтами:
Это значит, что слово «Россия» (6 букв) занимает 6 байт в Windows-1251 и 12 байт в UTF-8. Для коротких текстов разница незначительна, но при хранении миллионов строк в базе данных она становится заметной. С другой стороны, UTF-8 охватывает весь Unicode (более 140 000 символов), тогда как Windows-1251 ограничена 256 позициями - в неё не входят украинские Ї/і/ї, белорусское Ў, ни один иероглиф.
Перекодирование: как починить кракозябры
Когда текстовый файл отображается неверно, исправить ситуацию позволяет перекодирование - замена одной интерпретации байт другой. Инструменты командной строки для этого:
iconv -f windows-1251 -t utf-8 input.txt -o output.txt
В Python 3 перекодирование делается через bytes.decode:
data = open('file.txt', 'rb').read()
text = data.decode('windows-1251')
open('file_utf8.txt', 'w', encoding='utf-8').write(text)
Ключевой момент: нужно точно знать исходную кодировку. Если она неизвестна, помогает эвристический детектор - библиотека chardet в Python или утилита file -i в Linux. Однако короткие тексты (менее 100 байт) определяются ненадёжно: слишком мало статистики для различения Windows-1251 и KOI8-R (другой исторической кириллической кодировки).
В базах данных MySQL перекодирование сложнее: данные хранятся в столбцах с типом latin1 (так называли Windows-1251 в старых версиях MySQL), но приложение читает их как utf8. Исправление требует экспорта в SQL, замены SET NAMES latin1 на SET NAMES cp1251, а затем повторного импорта с параметром --default-character-set=cp1251. После этого все строки нужно сконвертировать командой CONVERT TO CHARACTER SET utf8mb4, которая физически переписывает байты в каждом столбце.
Не путайте «открыть в другой кодировке» и «конвертировать». Первое - лишь меняет интерпретацию тех же байт в редакторе (временно). Второе - физически переписывает байты на диске. При конвертировании важно сначала создать резервную копию.
Частые ошибки
- Считать, что Windows-1251 и cp1251 - разные кодировки. Это одно и то же: «cp» расшифровывается как code page (кодовая страница), cp1251 - синоним Windows-1251 в Python, Java, Linux.
- Забывать указывать кодировку в HTTP-заголовке. Браузер определяет кодировку из
Content-Type: text/html; charset=windows-1251; без этого он угадывает и часто ошибается. - Использовать
len()в Python 2 для подсчёта символов в строке Windows-1251. В Python 2len(s)возвращает длину в байтах, а не символах; корректно декодировать сначала:len(s.decode('cp1251')). - Смешивать в одном файле Windows-1251 и UTF-8. Конкатенация двух строк в разных кодировках даёт нечитаемый бинарный мусор.
- Предполагать, что «кириллица» = Windows-1251. Существуют ещё KOI8-R, KOI8-U, ISO-8859-5, IBM866 - у каждой свои коды для одних и тех же букв.
FAQ
Почему кириллические символы в Windows-1251 начинаются с 0xC0?
Это результат исторического решения Microsoft. Коды 0x80–0xBF были частично заняты псевдографикой и специальными символами (знаки валют, кавычки), а диапазон 0xC0–0xFF оказался свободен от конфликтов с Latin-1. Русские буквы разместили именно туда - подряд, чтобы упростить алфавитную сортировку. «Ё»/«ё» поместили отдельно (0xA8 и 0xB8), потому что таблица уже была заполнена до их стандартизации.
Можно ли хранить в Windows-1251 текст на двух языках, например русском и украинском?
Частично. Windows-1251 содержит большинство украинских букв (Г, Д, З, Л, Н и другие совпадают с русскими), но специфически украинские символы Ї (U+0457) и і (U+0456) отсутствуют в таблице. Для смешанных кириллических текстов с украинским, белорусским или болгарским необходима UTF-8 или специализированные кодировки (KOI8-U для украинского).
Как веб-браузер определяет кодировку страницы?
Браузер проверяет три источника по порядку убывания приоритета: HTTP-заголовок Content-Type (самый надёжный), тег <meta charset="..."> в начале HTML-документа, и наконец - эвристику (статистический анализ байтов). При наличии противоречий побеждает HTTP-заголовок. Именно поэтому тег <meta> помогает при чтении файлов с диска, но может быть проигнорирован сервером.
Коротко
Windows-1251 - однобайтовая кодировка, в которой каждый символ (включая кириллический) занимает ровно 1 байт. ASCII-зона (0x00–0x7F) совпадает с семибитным ASCII; кириллица А–Я и а–я занимают диапазоны 0xC0–0xDF и 0xE0–0xFF, Ё/ё стоят особняком (0xA8/0xB8). Главная проблема кодировки - несовместимость с Unicode: 256 позиций хватает только для одного языкового набора, отсюда кракозябры при смешении кодировок. Для новых проектов используется UTF-8, однако понимание Windows-1251 обязательно при работе с унаследованными системами и отладке текстовых данных.
Читайте также

Абстрактный класс и интерфейс: в чём отличие
Абстрактный класс и интерфейс: чем отличаются в ООП, когда наследовать поведение, а когда задавать контракт, как выбрать на примерах Java, C# и Python.

Алгоритм AdaBoost: как слабые классификаторы дают сильный
Алгоритм AdaBoost простыми словами: адаптивный бустинг, перевзвешивание объектов, формула веса классификатора, итоговый ансамбль и разбор шага на примере с формулами.

Алгоритм CatBoost: бустинг с обработкой категорий
Алгоритм CatBoost простыми словами: упорядоченный бустинг против сдвига прогноза, кодирование категориальных признаков через ordered target statistics, симметричные деревья и разбор типовых задач.