Страницы

Поиск по вопросам

среда, 10 октября 2018 г.

Что такое Unicode и как с ним связана UTF-8

Со страницей википедии про Unicode ознакомился, но так и не понял, хотя там и написано что это стандарт кодирования символов
Насколько я знаю то Unicode представлен следующим образом
0x00000000 — 0x0010F800
Есть такое утверждение, что UTF-16 = Unicode, так ли это?
UTF-16 представлен как 256*256 = 65 536 (без суррогатных пар), с суррогатными парами формула такая 2^20+2^16−2048 - тут не ясно как такая формула получилась, кто сведущ объясните (без суррогатных пар все ясно)
UTF-8 представлен следующим образом
4 байта (то что используется) 0x00000000 — 0x001FFFFF 6 байта (то что не используется) 0x00000000 — 0x7FFFFFFF
Тут как бы назревает вопрос а как мы можем переводить из UTF-8 в Unicode если Unicode кодирует в 2 раза меньше символов?
Что такое Unicode и зачем в него переводить допустим из той же самой UTF-8?
P.S Каша в голове, запутался уже X_X, помогите проявить ясность в голове


Ответ

Резюмируя написанное в комментариях и чате. Примечание: данный ответ не претендует на строгую спецификацию, а является объяснением «на пальцах» для улучшения понимания и может содержать неточности, а для разработки программ лучше читать не SO и даже не Википедию, а непосредственно сам стандарт
В контексте вопроса Unicode — это просто табличка символов и закреплённых за ними целых чисел (не каких-либо байт, а обычных человеческих чисел). (А ещё юникод описывает обработку символов и их преобразования друг в друга, но вопрос не про это.)
Кусочек этой таблички:

Эта табличка не указывает, как именно эти числа переводить в байты, которые можно было бы сохранить в компьютере. Но, чтобы хранить юникод в компьютере, кто-то должен указать, как их всё-таки переводить!

Возьмём числа из этой таблички символов и запихнём их в четыре байта так, чтобы четыре байта представляли беззнаковое целое число (unsigned int, uint32). То есть, например, из кода буквы «Я» 42F16 получаем байты 00 00 04 2F. Таким образом мы получили простейшую кодировку UTF-32 (UTF-32-BE1, UCS-4). Если интерпретировать эти полученные четыре байта как беззнаковое целое число, то мы получим номер символа в табличке Unicode.
С уточнением, что мы используем uint32, можно для себя считать, что UTF-32 = Unicode: каждое число кодируется-декодируется как есть без каких-либо преобразований.

Если мы возьмём числа из таблички и запихнём их в два байта, представляющие беззнаковое целое число (unsigned short, uint16), то мы получили бы первую версию Юникода, которая была в 1991 году. Тогда число символов было ограничено 65536, и все их коды можно было легко представить двумя байтами без дополнительных преобразований.
Но потом решили, что 65 тысяч это как-то мало, и увеличили максимальное количество символов до миллиона. Но ведь миллион в два байта уже никак не запихнёшь, и нужно взять больше байт на символ. Но так как уже успели появиться программы, рассчитывающие на эти самые два байта (например, в Java или Windows NT длина одного символа до сих пор 2 байта), то для сохранения хоть какой-то обратной совместимости из этих двух байт выдрали диапазон D80016..DFFF16 и сказали, что это суррогатные пары, занимающие по четыре байта и обрабатывающиеся по особому алгоритму для символов с кодом 1000016 и больше. И назвали всё это кодировкой UTF-16
Таким образом, в UTF-16 числа из таблички юникода, попадающие в диапазоны 000016..D7FF16 и E00016..FFFF16, записываются в два байта как есть, а символы с кодами 1000016 и больше записываются в виде суррогатных пар с использованием диапазона D80016..DFFF16 и занимают четыре байта. Так старые приложения, сделанные под первую двухбайтовую версию Юникода, смогли продолжить и дальше с ним работать, если не используются суррогатные пары.
Таким образом та же буква «Я» (код 42F16) представляется в UTF-16 (UTF-16-BE) как 04 2F, а символ «🔒» с кодом 1F51216, не влезающим в два байта, по алгоритму преобразуется в четыре байта D8 3D DD 12
Из-за этих самых суррогатных пар с кучей преобразований UTF-16 ≠ Unicode

UTF-8 была сделана для совместимости с ASCII: в ней коды символов от 0 до 7F16 записываются в один байт как есть, в итоге выдавая этот самый ASCII, а всё остальное кодируется по алгоритмам ещё более хитрым, чем в UTF-16, поэтому UTF-8 ≠ Unicode тоже.
Технически UTF-8 позволяет закодировать числа аж до двух миллиардов, однако Unicode ограничен миллионом символов, и стандартом установлено, что кодировать в UTF-8 числа, не входящие в допустимый диапазон Unicode, запрещено. Поэтому проблемы перевода из UTF-8 в Unicode чисел, не входящих в Unicode, просто нет: хорошо сделанное приложение в таких случаях выкинет ошибку или заменит всё недопустимое символом �.

В Notepad и Notepad++ можно наблюдать кодировку, названную «Unicode», но на самом деле это UTF-16-LE с BOM1
(клик для увеличения)


Самой распространённой кодировкой вроде как является UTF-8.

1 — числа, занимающие более одного байта, можно записывать с разным порядком байт: например, число 259 можно записать как 01 03 (big endian, BE) или как 03 01 (little endian, LE). То же самое применимо к символам в кодировках UTF-16, UTF-32 и некоторым другим (но не UTF-8). Вышеупомянутый 🔒 кодируется в UTF-16-LE неё как 3D D8 12 DD — в то время как в UTF-16-BE это будет D8 3D DD 12. BOM — это специальный символ FEFF16 в начале текста, который позволяет определить этот самый порядок байт — в big endian он пишется как FE FF, а в little endian как FF FE, а использование символа FFFE16 запрещено, что и позволяет определить порядок байт.

Комментариев нет:

Отправить комментарий