EssayAI
Блог
Блог
Математика и алгоритмы

Кодировка Unicode и UTF-8: как кодируются символы

7 июля 2026Время чтения: 9 минут
#unicode#utf-8#кодировка символов#кодовая точка#юникод

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

Зачем нужен Unicode

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

Кодовая точка и запись U+XXXX

Кодовую точку принято записывать в виде U+XXXX, где XXXX - номер символа в шестнадцатеричной системе. Буква A имеет кодовую точку U+0041 (десятичное 65 - то же значение, что и в ASCII), русская буква "я" - U+044F (1103), знак евро "€" - U+20AC (8364), иероглиф "中" - U+4E2D (20013), а эмодзи "😀" - U+1F600 (128512). Диапазон Unicode ограничен значением U+10FFFF, то есть 1 114 111 в десятичной записи - это исторический предел, о котором поговорим отдельно ниже. Именно кодовую точку, а не сам символ, обрабатывают алгоритмы кодирования: чтобы получить байты UTF-8, нужно взять двоичную запись этого числа и правильно разложить её биты по байтам.

Как устроена UTF-8: префиксы и продолжающие байты

UTF-8 - кодировка переменной длины: короткие, часто встречающиеся коды занимают меньше байт, редкие - больше. Правило простое и завязано на величину кодовой точки UU:

  • если U127U \le 127 (U+007F) - один байт вида 0xxxxxxx, где семь x - это сама кодовая точка в двоичном виде;
  • если U2047U \le 2047 (U+07FF) - два байта: первый 110xxxxx несёт пять старших бит, второй 10xxxxxx - шесть младших;
  • если U65535U \le 65535 (U+FFFF) - три байта: 1110xxxx, 10xxxxxx, 10xxxxxx, вместе они несут 16 значащих бит;
  • если U1114111U \le 1114111 (U+10FFFF) - четыре байта: 11110xxx, 10xxxxxx, 10xxxxxx, 10xxxxxx, вместе 21 бит.
Кодовая точка евро € (U+20AC = 8364) в двоичном виде делится на группы 4+6+6 бит; каждая группа садится в свой байт со своим префиксом - 1110xxxx для первого байта, 10xxxxxx для двух продолжающих

Первый байт всегда сообщает длину всей последовательности: по числу единиц перед первым нулём в его старшем полубайте сразу видно, сколько байт займёт символ - один, два, три или четыре. Все байты, кроме первого, начинаются с фиксированного префикса 10xxxxxx - это позволяет декодеру, открыв файл с любого места, мгновенно понять, что перед ним "хвост" многобайтового символа, а не начало нового. Такая избыточность - цена, которую UTF-8 платит за самосинхронизацию: даже если байты потеряны или файл открыт не с начала, декодер быстро находит границу следующего символа.

Разбор примера: символ € в UTF-8

Возьмём знак евро, кодовая точка U+20AC, то есть 8364 в десятичной записи. Двоичная запись этого числа - 10000010101100, ровно 14 значащих бит, и по правилу выше оно попадает в диапазон трёх байт (до 65535). Разбиваем 16 доступных бит на группы 4+6+6, дополняя старшую группу нулями: 0010 000010 101100. Теперь собираем байты:

байт 1=11100010=E2,байт 2=10000010=82,байт 3=10101100=AC.\text{байт 1} = 1110\,0010 = \text{E2}, \quad \text{байт 2} = 10\,000010 = 82, \quad \text{байт 3} = 10\,101100 = \text{AC}.

Итоговая последовательность - E2 82 AC в шестнадцатеричном виде, три байта. Именно такую тройку байт вы увидите, если открыть файл с этим символом в hex-редакторе. Обратный процесс, декодирование, работает зеркально: декодер видит первый байт с префиксом 1110, узнаёт по нему, что символ занимает три байта, забирает четыре бита из первого байта и по шесть из каждого продолжающего, склеивает их в число 8364 и смотрит это значение в таблице Unicode.

Кодовая точка эмодзи (U+1F600 = 128512) в 21-битном двоичном виде разбита фигурной скобкой на группы 3+6+6+6 бит и распределена по четырём байтам UTF-8 с префиксами 11110xxx и 10xxxxxx
Кодовая точка эмодзи (U+1F600 = 128512) в 21-битном двоичном виде разбита фигурной скобкой на группы 3+6+6+6 бит и распределена по четырём байтам UTF-8 с префиксами 11110xxx и 10xxxxxx

Тот же принцип работает и для эмодзи, только байт становится четыре: кодовая точка "😀" (U+1F600, 128512) лежит за пределами Basic Multilingual Plane - базовой плоскости, где помещаются почти все буквенные алфавиты, - поэтому попадает в диапазон четырёх байт с 21 значащим битом.

UTF-8 против UTF-16 и UTF-32

У Unicode есть и другие схемы кодирования байтов, и они не всегда дают тот же результат, что UTF-8. UTF-32 - самая простая по устройству: каждый символ занимает ровно четыре байта, без исключений. Это удобно для вычислений (номер символа в строке всегда можно найти прямым умножением на 4), но крайне расточительно для обычного текста на латинице или кириллице. UTF-16 - компромисс: символы из базовой плоскости (до U+FFFF, а это буквы почти всех живых языков) занимают два байта, а более редкие символы вроде эмодзи и старинных письменностей кодируются суррогатной парой - двумя двухбайтовыми "половинками", то есть тоже четырьмя байтами.

Из-за этого разные кодировки по-разному "весят" один и тот же текст. Латинское слово в UTF-8 займёт по одному байту на букву, а в UTF-16 или UTF-32 - вдвое-вчетверо больше. А вот слово "Привет" в UTF-8 займёт по два байта на букву (12 байт на шесть букв), и ровно столько же в UTF-16 - для кириллицы разница между этими двумя кодировками стирается, потому что все её буквы лежат в базовой плоскости. Для иероглифов и знака евро (три байта в UTF-8) UTF-16 иногда оказывается компактнее (два байта), поэтому веб отдаёт предпочтение UTF-8 в основном из-за полной обратной совместимости с ASCII и удобства работы с текстовыми протоколами, а не из-за абсолютной экономии байт на каждом символе.

Почему максимум - U+10FFFF

Кодовая точка Unicode искусственно ограничена значением U+10FFFF, хотя изначальная версия UTF-8 (1993 года) умела кодировать числа вплоть до 0x7FFFFFFF пятью и шестью байтами. Ограничение появилось из-за UTF-16: суррогатные пары строятся из двух 16-битных половинок, и арифметика этого механизма физически не может адресовать больше 2^20 дополнительных кодовых точек сверх базовой плоскости, что и даёт верхнюю границу U+10FFFF. Дополнительное следствие того же механизма - диапазон U+D800–U+DFFF (55 296–57 343) зарезервирован под сами половинки суррогатных пар и не может использоваться как кодовая точка настоящего символа: если такое значение попадает в декодер UTF-8, это ошибка данных. В 2003 году стандарт RFC 3629 официально урезал UTF-8 до четырёх байт и того же предела U+10FFFF, синхронизировав его с UTF-16 - так все схемы кодирования Unicode стали описывать один и тот же набор символов.

Частые ошибки

  • Путать символ и байт. "Длина строки" в символах и её размер в байтах - разные числа, если в строке есть кириллица, эмодзи или иероглифы: один символ может занимать от 1 до 4 байт.
  • Забывать про суррогатный диапазон. Значения U+D800–U+DFFF нельзя использовать как самостоятельную кодовую точку - они существуют только парами внутри UTF-16.
  • Путать длину группы бит с длиной байта. В байтах-продолжениях 10xxxxxx полезны только шесть младших бит, а не восемь - первые два бита это служебный префикс.
  • Проверять число байт по символу, а не по кодовой точке. Решает не то, "буква это или иероглиф", а числовое значение кодовой точки относительно границ 127, 2047 и 65535.
  • Путать UTF-8 с самим Unicode. Unicode - таблица номеров символов, UTF-8 - лишь один из способов записать эти номера байтами; есть ещё UTF-16 и UTF-32.

FAQ

Сколько байт занимает один символ в UTF-8? От одного до четырёх, в зависимости от величины кодовой точки: латиница и цифры - один байт, кириллица, греческий, знаки вроде € - два-три байта, эмодзи и редкие иероглифы - четыре байта.

Почему в UTF-8 первый байт начинается с разного числа единиц? Число единиц перед первым нулём в первом байте (110, 1110 или 11110) сразу сообщает декодеру, сколько байт занимает символ целиком - это позволяет читать текст, не заглядывая вперёд.

Чем UTF-8 лучше UTF-16? Для текста на латинице UTF-8 компактнее и полностью совместим с ASCII побайтово, поэтому это основная кодировка веба; для текста, где преобладают иероглифы, UTF-16 иногда даёт меньший размер файла.

Коротко

Unicode присваивает уникальный номер, кодовую точку U+XXXX, каждому символу любого языка, а UTF-8 превращает этот номер в один, два, три или четыре байта по строгим правилам с префиксами 0, 110/10, 1110/10 и 11110/10. Число байт растёт вместе с величиной кодовой точки - латиница укладывается в один байт, кириллица и большинство алфавитов в два-три, редкие символы и эмодзи в четыре. Верхняя граница U+10FFFF и запрет на диапазон U+D800–U+DFFF - не случайность, а следствие совместимости с UTF-16 через механизм суррогатных пар.

Доверьте текст нейросети EssayAI

Открыть EssayAI

Бесплатно, на русском языке и без VPN

Читайте также

Подсчёт символов в строке: длина, пробелы, байты

Подсчёт символов в строке: длина, пробелы, байты

Подсчёт символов в строке простыми словами: чем длина с пробелами отличается от длины без них, почему len со счётом эмодзи и кириллицы расходится, как считать символы на Python и JavaScript.

11 июня 20268 минут
Атрибуты сущности в ER-модели: пять типов и примеры

Атрибуты сущности в ER-модели: пять типов и примеры

Разбираем пять типов атрибутов сущности в ER-модели: простой, составной, ключевой, многозначный, производный, с примерами и переходом к столбцам и таблицам реляционной схемы.

7 июля 20269 минут
Циркуляция векторного поля по контуру: формула и смысл

Циркуляция векторного поля по контуру: формула и смысл

Циркуляция векторного поля по контуру: что это такое, как вычислить линейный интеграл по параметризации и через ротор поля с теоремой Грина, разбор типичных ошибок и примеров расчёта.

7 июля 20267 минут
Динамическое программирование: основы и идея мемоизации

Динамическое программирование: основы и идея мемоизации

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

7 июля 20267 минут
Начальные и центральные моменты случайной величины: формулы

Начальные и центральные моменты случайной величины: формулы

Разбираем начальные и центральные моменты случайной величины: как выразить дисперсию, асимметрию и эксцесс через ν_k и вычислить их на примере дискретного распределения.

7 июля 20268 минут
Натуральная величина сечения многогранника плоскостью

Натуральная величина сечения многогранника плоскостью

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

7 июля 20268 минут