Почему UTF-8 тратит несколько битов в своей кодировке

17

Согласно статье в Википедии , UTF-8 имеет такой формат:

Первый код Последний код Байты Байт 1 Байт 2 Байт 3 Байт 4
точка точка используется
U + 0000 U + 007F 1 0xxxxxxx
U + 0080 U + 07FF 2 110xxxxx 10xxxxxx
U + 0800 U + FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
U + 10000 U + 1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
x означает, что этот бит используется для выбора кодовой точки.

Это тратит два бита на каждый байт продолжения и один бит в первом байте. Почему кодировка UTF-8 не кодируется следующим образом?

Первый код Последний код Байты Байт 1 Байт 2 Байт 3
точка точка используется
U + 0000 U + 007F 1 0xxxxxxx
U + 0080 U + 3FFF 2 10xxxxxx xxxxxxxx
U + 0800 U + 1FFFFF 3 110xxxxx xxxxxxxx xxxxxxxx

Он сохранит один байт, когда кодовая точка находится вне базовой многоязычной плоскости или если кодовая точка находится в диапазоне [U + 800, U + 3FFF].

Почему UTF-8 не кодируется более эффективно?

qbt937
источник
3
cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt Предлагаемое кодирование аналогично исходному предложению FSS / UTF. Кен Томпсон и Роб Пайк хотели самосинхронизирующуюся собственность.
ниндзя
4
Кроме того, ваша кодировка, кажется, не гарантирует, что значения кода ASCII не появятся ни в одной части представления для не-ASCII символов. FSS / UTF и UTF-8 предназначены для работы с устаревшими программами (например, с теми, которые используют ASCII NUL и косую черту (разделитель пути) в качестве разделителей).
ниндзя

Ответы:

26

Это сделано для того, чтобы вы могли определить, когда находитесь в середине многобайтовой последовательности. Глядя на данные UTF-8, вы знаете, что если вы видите 10xxxxxx, что вы находитесь в середине многобайтового символа, и вам следует выполнять резервное копирование в потоке, пока вы не увидите ни то, 0xxxxxxни другое 11xxxxxx. Используя вашу схему, байты 2 или 3 могут легко получить паттерны типа 0xxxxxxxили11xxxxxx

Также имейте в виду, что объем сохраняемых данных полностью зависит от того, какой тип строковых данных вы кодируете. Для большей части текста, даже азиатского текста, вы редко, если когда-либо увидите четырехбайтовые символы с обычным текстом. Кроме того, наивные оценки людей о том, как будет выглядеть текст, часто ошибочны. У меня есть текст, локализованный для UTF-8, который включает строки на японском, китайском и корейском языках, но на самом деле русский язык занимает больше места. (Поскольку в наших азиатских строках часто встречаются латинские буквы с собственными именами, пунктуацией и т. Д., А среднее китайское слово составляет 1-3 символа, а среднее русское слово - много, много больше.)

Горт Робот
источник
Но со мной схема, если вы начинаете с местоположения, о котором известно, что он находится в начале символа, тогда вы можете сказать, сколько байтов в символе, и перейти к началу следующего символа.
qbt937
11
Конечно. Ваша схема более насыщена информацией, но не имеет важной функции, которую обеспечивает UTF-8. В целом люди предпочитают безопасность, поэтому UTF-8 возможен. Кроме того, чтобы действительно доказать, что ваша схема на самом деле более эффективна, вам нужно предоставлять статистику с использованием реального текста. Вы можете обнаружить, что в большинстве реальных текстов ваша схема экономит очень тривиальную сумму и, следовательно, экономия того не стоит.
Gort the Robot
3
Еще одна важная характеристика: если нет встроенной нулевой кодовой точки, то в строке нет встроенных нулей.
дедупликатор
Для тайского сценария необходимо разрешить 4 байта на печатный символ. Мало того, что они опоздали на вечеринку и поэтому получили кодовую группу с высоким номером. Многие вещи, которые при печати выглядят как один символ, на самом деле состоят из трех разных символов Юникода.
Джеймс Андерсон
@ qbt937: Используя вашу схему, как можно быстро отсканировать, чтобы узнать, содержит ли одна строка другую?
Суперкат
6

Официальный способ позволяет декодеру знать, когда он находится в середине кортежа, и он знает, как пропускать байты (или идти назад), пока байт не начинается с 0или 11; это предотвращает значения мусора, когда один байт поврежден.

чокнутый урод
источник
3

Короткий ответ: в вашем предложении не проводится различие между первым байтом и байтами продолжения.

Битовая комбинация в верхнем конце первого байта говорит вам, сколько байтов содержит фактический символ. Эти шаблоны также обеспечивают распознавание ошибок при разборе строки. Если вы читаете (казалось бы) первый байт символа и получаете 10xxxxxx, то вы знаете, что вы не синхронизированы.

Китано
источник
2

То, что не было упомянуто, - то, что, если у вас есть правильная последовательность кодовых точек и указатель, который гарантированно указывает на первый байт кодовой точки, с UTF-8 вы можете очень легко найти указатель на первый байт предыдущей кодовой точки (пропустите все байты, которые начинаются с 01xx xxxx). С вашей кодировкой это невозможно без потенциальной проверки всех байтов до начала строки.

Рассмотрим последовательности (2n + 2) байтов

0xxxxxxx
n times (10xxxxxx, 10xxxxxx)
0xxxxxxx

и

n times (10xxxxxx, 10xxxxxx)
(10xxxxxx, 0xxxxxxx)

Если у вас есть указатель на первый байт первой кодовой точки после этой последовательности, вы должны проверить все байты, чтобы выяснить, является ли последняя кодовая точка 0xxxxxxx или (10xxxxxx, 0xxxxxxx).

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

X where X < 128
YX where 128 ≤ Y < 236, X < 128
ZYY where 236 ≤ Z < 256, 0 ≤ Y < 236. 

Если один из предыдущих трех байтов равен ≥ 236, то это начало 3-байтовой последовательности, поскольку в любой допустимой 3-байтовой последовательности не может быть двух таких байтов. В противном случае, если один из двух предыдущих байтов равен ≥ 128, то это начало двухбайтовой последовательности. В противном случае предыдущий байт представляет собой один байт <128.

Поиск подстроки становится немного сложнее. Возможно, вы захотите исключить нулевые байты, чтобы строка содержала нулевой байт, только если она содержит нулевую кодовую точку.

gnasher729
источник
То, что не было упомянуто… - не совсем, как это следует непосредственно из наблюдения, сделанного в ответе @ratchet freak.
Петр Доброгост