Как работает «кодировка переменной ширины» UTF-8?

110

В стандарте unicode достаточно кодовых точек, поэтому вам нужно 4 байта для их хранения. Вот что делает кодировка UTF-32. Тем не менее, кодировка UTF-8 каким-то образом сжимает их в гораздо меньшие пространства, используя так называемое «кодирование переменной ширины».

Фактически, ему удается представить первые 127 символов US-ASCII всего одним байтом, который выглядит в точности как настоящий ASCII, поэтому вы можете интерпретировать много текста ascii, как если бы это был UTF-8, ничего не делая с ним. Аккуратный трюк. Итак, как это работает?

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

dsimard
источник
8
Для прямого Unicode не требуется 32 бита для кодирования всех его кодовых точек. Когда-то они действительно претендовали на такое количество возможных кодовых точек, но после того, как UTF-8 взлетел, они намеренно ограничили себя 21 битом, чтобы UTF-8 никогда не превышал 4 байта на символ. Unicode в настоящее время требует всего 17 бит для хранения всех возможных кодовых точек. Без этого ограничения UTF-8 мог бы увеличиться до 6 байтов на символ.
Уоррен Янг,
@Warren: в основном точный, но Unicode - это 21-битный код (от U + 0000 до U + 10FFFF).
Джонатан Леффлер,
2
@Warren: UTF-8 с ограничением по 4 байта мог поддерживать до U + 1FFFFF. Ограничение на U + 10FFFF было сделано ради UTF-16.
dan04
@ dan04 Есть ли у нас какое-нибудь простое объяснение того, как он ограничен U + 10FFFF UTF-16? Было бы неплохо узнать об этом побольше.
A-letubby
@ A-letubby: поскольку «суррогатные» коды UTF-16 распределяются таким образом, что есть 1024 суррогата ведущего и 1024 суррогата следа (и они могут использоваться только парами), чтобы сделать 2 ^ 20 (около миллиона) дополнительных символов доступны за пределами BMP. При добавлении к 2 ^ 16 символам, доступным в BMP, это дает 0x110000 возможных символов.
dan04

Ответы:

129

Каждый байт начинается с нескольких битов, которые говорят вам, является ли это однобайтовой кодовой точкой, многобайтовой кодовой точкой или продолжением многобайтовой кодовой точки. Как это:

0xxx xxxx    A single-byte US-ASCII code (from the first 127 characters)

Каждая многобайтовая кодовая точка начинается с нескольких битов, которые, по сути, говорят: «Эй, вам нужно также прочитать следующий байт (или два, или три), чтобы понять, кто я». Они есть:

110x xxxx    One more byte follows
1110 xxxx    Two more bytes follow
1111 0xxx    Three more bytes follow

Наконец, все байты, следующие за этими стартовыми кодами, выглядят следующим образом:

10xx xxxx    A continuation of one of the multi-byte characters

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

dsimard
источник
14
В этой истории есть еще кое-что - потому что кодировка должна быть самой короткой из возможных кодировок для символа, что в конечном итоге означает, что байты 0xC0 и 0xC1 не могут появляться, например, в UTF-8; и, по сути, 0xF5..0xFF тоже не может. См. Часто задаваемые вопросы по UTF-8 на unicode.org/faq/utf_bom.html или unicode.org/versions/Unicode5.2.0/ch03.pdf
Джонатан Леффлер,
2
Почему он не мог сказать только один символ next char is continuation? Если бы мы получили 3-байтовый символ, это было бы так:, 1xxxxxxx 1xxxxxxx 0xxxxxxxпоэтому было бы потрачено меньше места.
9
@Soaku делает UTF-8 так называемым "самосинхронизирующимся" кодом. Это означает, что если из-за ошибок части последовательности отсутствуют, это можно обнаружить и отбросить все, что было искажено. Если вы читаете байт, который начинается с 10xx, а предшествующего «начального» байта нет, вы можете отбросить его, поскольку он не имеет смысла. Если у вас была описанная вами система, и один из первых байтов был потерян, вы могли бы получить другой допустимый символ без каких-либо признаков ошибки. Это также упростит поиск следующего допустимого символа, а также исправит пропущенные байты «продолжения».
htmlcoderexe 05
9

RFC3629 - UTF-8, формат преобразования ISO 10646, является здесь окончательным авторитетом и содержит все пояснения.

Короче говоря, несколько битов в каждом байте последовательности от 1 до 4 байтов в кодировке UTF-8, представляющей один символ, используются для указания того, является ли он конечным байтом, ведущим байтом и, если да, то сколько байтов следует за ним. Остальные биты содержат полезную нагрузку.

ажеглов
источник
1
Умммм, глупый я, я думал, что стандарт Unicode был окончательным авторитетом в UTF-8
Джон Мачин
6
Стандарт Unicode определяет сам Unicode. Он не определяет различные методы, сегодняшние и будущие, которые можно использовать для кодирования текстов Unicode для различных целей (таких как хранение и транспортировка). UTF-8 - один из этих методов, и ссылка выше относится к документу, в котором он определяется.
ажеглов 01
1
RFC3629, стр. 3, раздел 3. гласит: «UTF-8 определяется стандартом Unicode».
Джон Мачин
Поиск ссылок на unicode.org привел меня к разделу 3.9 стандарта Unicode, в частности к определению D92 (а также косвенно D86). Я понятия не имею, в какой степени эта ссылка будет полезна при выпуске новых версий, но я предполагаю, что они хотят, чтобы идентификаторы разделов и определений были стабильными для разных версий.
tripleee 05
4

UTF-8 был еще одной системой для хранения вашей строки кодовых точек Unicode, этих магических чисел U +, в памяти с использованием 8-битных байтов. В UTF-8 каждая кодовая точка от 0 до 127 хранится в одном байте. Только кодовые точки 128 и выше сохраняются с использованием 2, 3, фактически до 6 байтов.

Отрывок из «Абсолютного минимума». Каждый разработчик программного обеспечения должен абсолютно точно знать о Unicode и наборах символов (без оправданий!)

Андрей
источник
Это хорошая статья, но похоже, что Джоэл ошибается относительно максимальной длины последовательности; страница Википедии показывает только 1..4 байта на символ.
расслабьтесь,
4
Как я сказал выше, когда UTF-8 был впервые создан, Unicode претендовал на 32-битное кодирование не потому, что им это действительно нужно, а только потому, что 32-битное значение является удобным, и они уже прошли мимо предыдущий предел 16-битных символов. После того, как UTF-8 стал популярным, они решили навсегда ограничить максимальное количество кодовых точек до 2 ^ 21, что является самым большим значением, которое вы можете кодировать с 4 байтами схемы UTF-8. В Юникоде по-прежнему меньше 2 ^ 17 символов, поэтому мы можем более чем в четыре раза увеличить количество символов в Юникоде с помощью этой новой схемы.
Уоррен Янг,
Хорошо, но не объяснение, запрошенное OP.
Nishant
2
Это не ответ на вопрос.
Корай Тугай