Кодировка UTF-16 - Зачем использовать сложные суррогатные пары? - PullRequest
1 голос
/ 21 января 2020

Я работал над схемами кодирования строк, и пока я изучал, как работает UTF-16, у меня возник вопрос. Зачем использовать сложные суррогатные пары для представления 21-битной кодовой точки? Почему бы просто не сохранить биты в первой кодовой единице, а оставшиеся биты - во второй кодовой единице? Я что-то пропустил! Есть ли проблема для хранения битов напрямую, как мы это делали в UTF-8?

Пример того, о чем я думаю:

Символ '?'

Соответствующий код точка: 128579 (десятичное число)
двоичная форма: 1 1111 0110 0100 0011 (17 бит)
это 17-разрядная кодовая точка.

  • На основе схем UTF-8 она будет представлен как:

    240 : 11110 000
    159 : 10 011111
    153 : 10 011001
    131 : 10 000011
    
  • В UTF-16, почему бы не сделать что-то похожее, вместо использования суррогатных пар:

    49159 : 110 0 0000 0000 0111
    30275 : 01 11 0110 0100 0011
    

1 Ответ

4 голосов
/ 22 января 2020

Предлагаемая альтернатива UTF-16

Я думаю, что вы предлагаете альтернативный формат с использованием 16-битных кодовых единиц, аналогичных схеме кода UTF-8 - давайте обозначим его UTF-EMF-16.

В вашей схеме UTF-EMF-16 кодовые точки от U + 0000 до U + 7FFF будут кодироваться как одна 16-битная единица, причем MSB (старший значащий бит) всегда равен нулю. Затем вы должны зарезервировать 16-битные блоки с 2 старшими значащими битами, установленными на 10 как «блоки продолжения», с 14 битами данных полезной нагрузки. Затем вы должны кодировать кодовые точки от U + 8000 до U + 10FFFF (текущая максимальная кодовая точка Unicode) в 16-битных единицах с тремя старшими значащими битами, установленными на 110, и до 13 бит данных полезной нагрузки. С Unicode, как определено в настоящее время (U + 0000 .. U + 10FFFF), вам никогда не понадобится больше 7 из 13 установленных битов.

U+0000 .. U+7FFF   — One 16-bit unit: values 0x0000 .. 0x7FFF
U+8000 .. U+10FFF  — Two 16-bit units:
                     1. First unit  0xC000 .. 0xC043
                     2. Second unit 0x8000 .. 0xBFFF

Для вашего примера кодовой точки, U + 1F683 (двоичный : 1 1111 0110 0100 0011):

First unit:  1100 0000 0000 0111 = 0xC007
Second unit: 1011 0110 0100 0011 = 0xB643

Второй блок отличается от вашего примера обращением двух старших значащих бит: от 01 в вашем примере до 10 в моем.

Почему такая схема не использовалась в UTF-16

Такую схему можно заставить работать. Это однозначно. Он может вместить гораздо больше символов, чем позволяет в настоящее время Юникод. UTF-8 может быть изменен, чтобы стать UTF-EMF-8, чтобы он мог обрабатывать тот же расширенный диапазон, при этом некоторым символам требуется 5 байтов вместо текущего максимального значения 4 байта. UTF-EMF-8 с 5 байтами будет кодировать до 26 бит; UTF-EMF-16 может кодировать 27 бит, но его следует ограничить 26 битами (примерно 64 миллиона кодовых точек вместо чуть более 1 миллиона). Итак, почему это не было или что-то очень похожее, принятое?

Ответ очень распространенный - история (плюс обратная совместимость).

Когда Unicode был впервые определен, он был надеялся или верил, что 16-битного кодового набора будет достаточно. Кодирование UCS2 было разработано с использованием 16-битных значений, и многие значения в диапазоне 0x8000 .. 0xFFFF имели значения. Например, U + FEFF является меткой порядка байтов.

Когда необходимо было расширить схему Unicode, чтобы сделать Unicode большим набором кодов, было много определенных символов с битами 10 и 110 шаблоны в старших разрядах, поэтому обратная совместимость означала, что описанная выше схема UTF-EMF-16 не могла использоваться для UTF-16 без нарушения совместимости с UCS2, что было бы серьезной проблемой.

Следовательно стандартизаторы выбрали альтернативную схему, в которой есть высокие суррогаты и низкие суррогаты.

0xD800 .. 0xDBFF   High surrogates (most signicant bits of 21-bit value)
0xDC00 .. 0xDFFF   Low surrogates (less significant bits of 21-bit value)

Низкий диапазон суррогатов обеспечивает хранение 10 бит данных - префикс 1101 11 использует 6 из 16 бит. Большой диапазон суррогатов также обеспечивает хранение 10 бит данных - префикс 1101 10 также использует 6 из 16 битов. Но поскольку BMP (многоязычная плоскость Basi c - U + 0000 .. U + FFFF) не нужно кодировать двумя 16-битными модулями, кодирование UTF-16 вычитает 1 из данных высокого порядка, и поэтому может использоваться для кодирования U + 10000 .. U + 10FFFF. (Обратите внимание, что хотя Unicode является 21-битной кодировкой, не все 21-битные (без знака) числа являются действительными точками кода Unicode. Значения от 0x110000 .. 0x1FFFFF являются 21-битными числами, но не являются частью Unicode.)

Из Unicode FAQ - UTF-8, UTF-16, UTF-32 и BOM :

В: Какой алгоритм преобразования из UTF-16 в символ коды?

A: Стандарт Unicode раньше содержал короткий алгоритм, теперь есть только таблица распределения битов. Вот три коротких фрагмента кода, которые переводят информацию из таблицы распределения битов в C код, который преобразуется в и из UTF-16.

Используя следующие определения типов

typedef unsigned int16 UTF16;
typedef unsigned int32 UTF32;

первый фрагмент вычисляет старший (или ведущий) суррогат из кода символа C.

const UTF16 HI_SURROGATE_START = 0xD800

UTF16 X = (UTF16) C;
UTF32 U = (C >> 16) & ((1 << 5) - 1);
UTF16 W = (UTF16) U - 1;
UTF16 HiSurrogate = HI_SURROGATE_START | (W << 6) | X >> 10;

, где X, U и W соответствуют меткам, используемым в таблице 3-5 Распределение битов UTF-16. Следующий фрагмент делает то же самое для низкого суррогата.

const UTF16 LO_SURROGATE_START = 0xDC00

UTF16 X = (UTF16) C;
UTF16 LoSurrogate = (UTF16) (LO_SURROGATE_START | X & ((1 << 10) - 1));

И, наконец, обратное, где hi и lo - суррогатное значение high и low, а C результирующий символ

UTF32 X = (hi & ((1 << 6) -1)) << 10 | lo & ((1 << 10) -1);
UTF32 W = (hi >> 6) & ((1 << 5) - 1);
UTF32 U = W + 1;

UTF32 C = U << 16 | X;

Вызывающий абонент должен убедиться, что C, hi и lo находятся в соответствующих диапазонах. [

...