Символьная кодировка — это правило, по которому кодовой точке символа сопоставляется последовательность байт для хранения и передачи текста. Кодовую точку задает набор символов — перечень знаков с присвоенными им номерами. Одному и тому же набору символов могут соответствовать разные кодировки: кириллица в Windows-1251 занимает 1 байт на символ, а в UTF-8 — обычно 2 байта. Разберу основные символьные кодировки от ASCII до Unicode, покажу их различия и объясню, почему из-за путаницы между ними на экране появляются кракозябры.
Содержание
Набор символов, кодовая точка, кодировка: не путать
Три термина легко смешать, а они означают разное:
| Термин | Что это | Пример |
|---|---|---|
| Набор символов (character set) | Перечень знаков с присвоенными им номерами | Кириллица, латиница, ASCII-набор |
| Кодовая точка (code point) | Номер конкретного символа в наборе | U+042F — точка для буквы «Я» |
| Кодировка (encoding) | Правило перевода кодовой точки в байты | UTF-8, Windows-1251, KOI8-R |
| Кодовая единица (code unit) | Минимальный блок байт, из которых кодировка собирает точку | 1 байт в UTF-8, 2 байта в UTF-16 |
Сквозная цепочка на одном символе: буква «Я» — это кодовая точка U+042F в Unicode. В кодировке UTF-8 она превращается в 2 байта, в Windows-1251 — в 1 байт, а сами байты в этих кодировках разные:
ch = "Я"
print(ch.encode("utf-8")) # b'\xd0\xaf' - 2 байта
print(ch.encode("windows-1251")) # b'\xdf' - 1 байт
Вывод: b'\xd0\xaf' и b'\xdf' — это два разных байтовых представления одной и той же кодовой точки. Если прочитать вторые байты как UTF-8, получится не «Я», а ошибка или другой знак — в этом суть кракозябр (разберу ниже).
ASCII: база для всего остального
ASCII (American Standard Code for Information Interchange) — стандарт кодирования, принятый в 1963 году Американской ассоциацией по стандартам (ASA, позже переименована в ANSI). Изначально 7-битный: под него отводится 128 кодовых точек (0-127).
В ASCII входят:
— управляющие символы (0-31 и 127) — для оборудования, а не для печати;
— цифры и знаки препинания;
— заглавные и строчные буквы латинского алфавита.
Национальных алфавитов, включая кириллицу, в базовом ASCII нет — для них позже появились расширения и отдельные кодировки. Важный практический факт: первые 128 кодовых точек Unicode и первые 128 байт-значений UTF-8 совпадают побайтно с ASCII — поэтому текст, целиком состоящий из латиницы и цифр, одинаково читается и в ASCII, и в UTF-8.
Расширенные однобайтовые кодировки: CP866, KOI8-R, Windows-1251
Когда байт стал 8-битным, для второй половины таблицы (128-255) появились десятки региональных кодировок. Для кириллицы прижились три:
| Кодировка | Байт на символ | Где применялась | Статус сейчас |
|---|---|---|---|
| CP866 | 1 | MS-DOS, консоль Windows (legacy) | Практически не встречается вне старого ПО |
| KOI8-R | 1 | Почта и Unix-системы 1990-х | Изредка в старых письмах/логах |
| Windows-1251 | 1 | Кириллические версии Windows, веб-страницы 1990-2000-х | Встречается в старых сайтах и файлах, вытеснена UTF-8 |
Все три — упрощенная модель: они кодируют только один язык (русский плюс несколько родственных) и работают, пока и автор, и читатель текста используют одну и ту же таблицу. Как только кодировки расходятся, байты остаются те же, а символы, в которые их разворачивает получатель, — другие.
В KOI8-R кириллица расположена не по алфавиту: буквы стоят на местах, близких по начертанию к латинским (историческое решение для совместимости с транслитерацией по 7-битным каналам). В CP866 и Windows-1251 порядок ближе к алфавитному, но раскладки все равно разные — отсюда несовместимость байт напрямую между всеми тремя.
Откуда берутся кракозябры
Кракозябры — это не порча данных, а рассинхронизация: байты не менялись, но программа читает их не той таблицей, которой они были записаны. Пример на Python — записываю строку в Windows-1251, а читаю как KOI8-R:
original = "Привет"
raw_bytes = original.encode("windows-1251")
broken = raw_bytes.decode("koi8-r")
print(broken)
Неверный результат: оПХБЕР (проверено запуском кода выше). Исправление — декодировать теми же байтами, но правильной кодировкой:
fixed = raw_bytes.decode("windows-1251")
print(fixed) # Привет
Итог: если текст превратился в кракозябры, сами байты почти всегда целы. Задача — определить, в какой кодировке они были записаны, и раскодировать заново той же кодировкой. Автоматическое определение кодировки без явного указания (библиотеки вроде chardet) работает по статистической эвристике и не дает 100% гарантии, особенно на коротких текстах.
Unicode: UCS и семейство UTF
Unicode — стандарт, который присваивает уникальную кодовую точку каждому символу вне зависимости от языка, включая иероглифы, математические знаки и нотные символы. Разработку с 1991 года ведет некоммерческий Unicode Consortium. Количество назначенных кодовых точек растет с каждой версией стандарта — точное число на конкретный момент лучше сверять с сайтом консорциума, а не считать постоянной величиной.
Здесь важно развести два слоя, которые в обиходе называют одним словом «Юникод»:
— UCS (universal character set) — сама таблица кодовых точек, куда каждому символу присвоен номер вида U+XXXX;
— UTF (Unicode Transformation Format) — семейство кодировок, которые превращают эти номера в реальные байты для хранения и передачи.
Кодовая точка одна, а способов ее записать байтами — несколько, и они дают разный результат:
| Кодировка | Байт на символ | Особенность | Типичное применение |
|---|---|---|---|
| UTF-8 | 1-4, переменная длина | Первые 128 точек совпадают с ASCII; нет проблемы порядка байт | Веб, файлы, API — стандарт по умолчанию |
| UTF-16 | 2 или 4 (суррогатные пары для редких символов) | Зависит от порядка байт (little/big-endian) | Внутреннее представление строк в Windows API, Java, JavaScript |
| UTF-32 | 4, фиксированная длина | Простая индексация символов, но самый большой объем | Внутренние вычисления, редко для хранения/передачи |
Пример вывода для одной и той же буквы «Я» (U+042F) в трех кодировках, с проговоренным результатом:
ch = "Я"
print(ch.encode("utf-8")) # b'\xd0\xaf' - 2 байта
print(ch.encode("utf-16-le")) # b'/\x04' - 2 байта, little-endian
print(ch.encode("utf-32-le")) # b'/\x04\x00\x00' - 4 байта, дополнены нулями
Порядок байт в UTF-16/UTF-32 зависит от платформы, поэтому в коде, где это важно, стоит явно проверять или задавать endianness, а не полагаться на умолчание:
import sys
print(sys.byteorder) # 'little' на большинстве современных ПК, но не гарантированно
Это упрощенная демонстрация принципа: реальные текстовые процессоры дополнительно учитывают BOM (byte order mark) и обработку суррогатных пар для символов за пределами базовой многоязыковой плоскости — в статью эти детали не помещаются, но важно знать об их существовании при работе с файлами в UTF-16.
Как выбрать кодировку на практике
Короткое правило на 2026 год: для новых текстовых файлов, веб-страниц и API по умолчанию берите UTF-8 — он совместим с ASCII, не имеет проблемы порядка байт и поддерживает любой язык одной кодировкой. Однобайтовые кодировки (Windows-1251, KOI8-R, CP866) имеет смысл учитывать только при работе с legacy-файлами и старыми системами, где их меняют осознанно, а не потому что «так исторически сложилось».
Выводы
- Кодовая точка (номер символа), набор символов (перечень точек) и кодировка (правило перевода в байты) — три разных понятия, их не стоит называть одним словом «кодировка символов».
- ASCII — основа: 128 точек, первые 7 бит совпадают с началом любой современной кодировки на основе Unicode.
- CP866, KOI8-R и Windows-1251 — однобайтовые кодировки кириллицы; они несовместимы между собой на уровне байт, отсюда кракозябры при путанице.
- Кракозябры чинятся не переписыванием текста, а декодированием тех же байт правильной кодировкой.
- UTF-8, UTF-16 и UTF-32 — три способа записать один и тот же Unicode-код байтами; для новых проектов по умолчанию берут UTF-8.
Где применяется / связь с практикой
Освойте тему на практике
Работа с кодировками — обязательный навык при обработке текста, парсинге файлов и веб-разработке на Python: чтение файла в неправильной кодировке или неверная сериализация JSON — частая причина UnicodeDecodeError в проде. Разбор строк, байтовых объектов и метода encode/decode входит в базовый курс Python — там эти темы разбираются на практических задачах. Потренироваться на живых примерах и задать вопросы преподавателю можно на открытых уроках Otus.
Смежные темы: Типы данных в языках программирования.
FAQ
Какая кодировка используется в интернете по умолчанию?
UTF-8 — рекомендация W3C/WHATWG и фактический стандарт для новых веб-страниц и API; именно ее стоит указывать в <meta charset> и заголовках ответа, если нет причины использовать другую.
Можно ли определить кодировку файла автоматически, если нет BOM?
Только эвристически: библиотеки вроде chardet анализируют статистику байт и предполагают наиболее вероятную кодировку, но не гарантируют результат, особенно на коротких или смешанных текстах. Надежнее знать кодировку заранее из источника файла.
Почему при открытии старого файла в текстовом редакторе вместо кириллицы видны случайные символы?
Это тот же случай рассинхронизации: файл записан в одной однобайтовой кодировке (например, CP866), а редактор по умолчанию открывает его в другой (например, Windows-1251 или UTF-8) — нужно вручную указать редактору исходную кодировку.



