Кодировочные точки, единицы, байты и видимые символы

Разграничение кодировочных точек, единиц кодирования, байтов и видимых символов с вычисленными примерами для UTF-16 и UTF-32.

Кодировочные точки, единицы, байты и видимые символы

Назначение

Четыре термина, которые часто смешивают, описывают разные уровни представления текста. Кодировочная точка задаёт позицию символа в едином пространстве Юникод. Единица кодирования — это фиксированный по разрядности фрагмент, которым конкретная кодировка оперирует при записи. Байт — минимальная адресуемая величина в памяти. Видимый символ, или графемный кластер, — то, что человек воспринимает как один знак на экране. Разница между ними определяет, как считаются длины строк, как выполняется поиск и как хранятся данные.

Кодировочные точки и скалярные значения

Диапазон скалярных значений Юникод простирается от U+0000 до U+10FFFF, за вычетом блока суррогатов U+D800 по U+DFFF. В этом пространстве содержится 1114112 позиций. Это число позиций, а не число назначенных символов: многие позиции остаются свободными для будущих дополнений. В 32-битном хранилище могут оказаться значения, не являющиеся валидными скалярами, поэтому само по себе 32-битное поле не гарантирует корректный символ.

Единицы кодирования

Единица определяется конкретной кодировкой, а не языком программирования. UTF-16 использует 16-битные единицы: скалярные значения основного блока занимают одну единицу, а дополнительные значения требуют пары суррогатов, то есть двух единиц. UTF-32 хранит каждое скалярное значение в одной 32-битной единице. Байты же появляются при записи этих единиц в память: кодирование в UTF-8 использует от одного до четырёх байтов на скаляр, причём только значения из диапазона ASCII занимают ровно один байт. Программные интерфейсы могут индексировать строки по байтам, по единицам, по скалярам или по графемным кластерам, и выбор зависит от реализации.

Графемные кластеры и нормализация

Один видимый символ может состоять из нескольких скалярных значений. Пример: буква е с острым акцентом в форме нормализации NFC записывается одним скаляром U+00E9, а в форме NFD — двумя скалярами, U+0065 и U+0301. Оба представления содержат два варианта одного графемного кластера. При сравнении строк или выделении слов важно учитывать, что количество скаляров и количество видимых знаков не совпадают.

Пример с эмодзи

Скаляр U+1F600, улыбающееся лицо, лежит в дополнительном блоке. В UTF-16 он записывается парой суррогатов D83D DE00, то есть двумя 16-битными единицами; в little-endian порядок байтов даёт последовательность 3D D8 00 DE. В UTF-32 тот же скаляр занимает одну 32-битную единицу, а little-endian запись выглядит как 00 F6 01 00. Один видимый символ, две единицы UTF-16, четыре байта в обоих случаях.

Порядок байтов

Little-endian описывает порядок байтов фиксированного числового значения: младший байт записывается первым. Это не чтение шестнадцатеричного числа справа налево и не перестановка десятичных цифр. Если байты 01 04 00 00 интерпретировать как big-endian целое, получится 17039360, значение, не являющееся валидным скаляром Юникод. Ошибка в порядке байтов превращает корректный символ в бессмысленный код.