✨ Интерактивный разбор

Кодировки под микроскопом

«Привет» → Привет. Каждый, кто хоть раз открывал русский текст в неправильной программе, видел этот кошмар. Но что здесь произошло? Спойлер: никто ничего не испортил — просто байты одного алфавита прочитали по правилам другого.

Одна строка — два взгляда live
UTF-8 Привет, мир!
CP1251 Привет, мир!
1 Зачем это всё

Компьютер не понимает буквы

Внутри компьютера нет букв. Есть только числа. Буква «А» — это число 65, буква «Я» — число 1071, а любимый всеми эмодзи «😀» — вообще 128512. Каждая буква, цифра и знак — это просто кодовая точка.

Кодировка — это правило, как превратить номер символа в байты и обратно. Есть кодировки однобайтовые (один символ = один байт) и многобайтовые (один символ = от 1 до 4 байт). Именно из-за этого возникает 90% всей путаницы.

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

2 Байтовый инспектор

Введите текст — увидите его байты

Каждый байт — это 8 бит, то есть число от 0 до 255. В 16-ричном виде это два символа: D0, 9F и так далее. Смотрите, как одна и та же фраза превращается в совершенно разные байты в зависимости от выбранной кодировки.

UTF-8Стандарт современного веба · переменная длина
—
UTF-16JavaScript, Windows API · 2 или 4 байта
—
CP1251Windows, русская · 1 байт на символ
—
KOI8-RСоветская разработка · 1 байт на символ
—
ASCII (латиница, цифры, знаки) Первый байт кириллицы в UTF-8 Продолжающий байт UTF-8 Эмодзи (4 байта в UTF-8)
3 Кракозябры

Как «Привет» превращается в «ÐŸÑ€Ð¸Ð²ÐµÑ‚»

Вот и разгадка. Кто-то записал текст в одной кодировке, а прочитал в другой. Например, страница была в UTF-8, но браузер по ошибке решил, что она в CP1251. Байты правильные — а буквы из них получаются чужие. Выберите пару кодировок и посмотрите на результат.

Записано в UTF-8

Привет, мир!

Прочитано как CP1251

Привет, мир!
↓
4 Сколько байт в символе?

Эмодзи — это прожорливые монстры

В UTF-8 разные символы занимают разное количество байтов. Латиница — 1 байт, кириллица — 2, а вот эмодзи — целых 4. Из-за этого счётчик символов в приложениях иногда врёт: «😀» может считаться как 2, 4 или 7 символов — зависит от того, что именно считать.

A
Латинская «A»
1 байт в UTF-8
41
А
Кириллическая «А»
2 байта в UTF-8
D0 90
😀
Эмодзи «Улыбка»
4 байта в UTF-8
F0 9F 98 80
❤️
Сердце с «искрами»
6 байт в UTF-8
два символа + модификатор
👨‍👩‍👧
Семья
18 байт в UTF-8
три эмодзи + два соединителя
🇷🇺
Флаг России
8 байт в UTF-8
два «региональных» символа

Именно поэтому «👨‍👩‍👧» может влезть в твит с лимитом 140 символов как 1 «символ», а может съесть сразу 5 — зависит от того, что именно считать: сколько байтов, сколько кодовых точек или сколько «видимых» глифов.

4.5 Что такое «символ»

Байт, кодовая точка и глиф — три разных зверя

Слово «символ» в программировании означает три совершенно разные вещи, и путаница между ними — источник половины проблем с кодировками. Давайте разложим по полочкам.

1

Байт

Что реально лежит в файле

Физическая единица. Восемь бит. Именно байты летят по сети и хранятся на диске. Одна буква может занимать 1, 2, 3 или 4 байта — зависит от кодировки и символа.

Для «А»: D0 90 — 2 байта в UTF-8
2

Кодовая точка

Номер символа в Unicode

Абстрактное число. У каждого символа есть свой номер: «A» — это 65, «Я» — 1071, «😀» — 128512. Записывается как U+XXXX. Одна кодовая точка — один «настоящий» символ.

Для «А»: U+0410 — один символ
3

Глиф

Как символ выглядит на экране

Картинка. То, что вы видите. Одна и та же кодовая точка может рисоваться по-разному в разных шрифтах — это и есть разные глифы. Более того, один глиф может склеиваться из нескольких кодовых точек.

Для «А»: А А А — 3 глифа, 1 кодовая точка
🏠

Аналогия: письмо в конверте

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

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

Сравнение на примерах

Один и тот же текст можно посчитать тремя способами — и получить три разных ответа. Вот почему счётчики символов в разных приложениях показывают разные числа.

Текст Байт (UTF-8) Кодовых точек Глифов
A 1 1 1
А (кириллица) 2 1 1
😀 4 1 1
👨‍👩‍👧 (семья) 18 5 1
🇷🇺 (флаг) 8 2 1
Привет 12 6 6

💡 Забавный факт: «👨‍👩‍👧» в твите считается как 1 символ, а в некоторых SMS — как 5. Никто не обманывает — просто все считают по-своему.

5 Историческая несправедливость

Почему «Ё» иногда ломает всё

Ё

Буква, которую все забывают

Буква «Ё» — единственная в русском алфавите, которая не попала в «основную» раскладку клавиатуры и часто заменяется на «Е». Из-за этого она до сих пор приносит программистам головную боль:

В CP1251 (старая кодировка Windows) буква «Ё» стоит не рядом с «Е» в таблице — её код 0xA8, а «Е» — 0xC5. В KOI8-R — снова другое место. В Unicode — своя кодовая точка U+0401.

В итоге при конвертации между старыми кодировками именно «Ё» чаще всего превращается в «?» или «�». Хорошо, что UTF-8 объединила весь мир — но рефлекс «написать Е вместо Ё» остался.

6 Что делать

Правила, которые спасают

Если вы работаете с текстом, базами данных или сайтами — эти правила избавят от 90% проблем с кодировками.

✅

Всегда используйте UTF-8

Это стандарт современного веба. Другие кодировки — только для совместимости с legacy-системами.

📝

Указывайте кодировку явно

В HTML — <meta charset="utf-8">. В базах данных — CHARACTER SET utf8mb4. Не полагайтесь на «а вдруг угадает».

🔍

Если видите кракозябры — не паникуйте

Данные, скорее всего, целы. Просто перекодируйте их из одной кодировки в другую — обычно туда и обратно.

🌐

UTF-8 ≠ ASCII

Для латиницы, цифр и базовых знаков UTF-8 совпадает с ASCII — это сделано специально. Для всего остального — нет.

🎯

Учитывайте разные единицы

«Символ» может быть байтом, кодовой точкой или глифом. Если важен подсчёт — уточняйте, что именно считаете.

🔧

Используйте утилиту file -i

В Linux команда file -i filename покажет кодировку файла. Многие кодировки имеют BOM — невидимый маркер в начале файла.