Это вольный перевод статьи Unconfusing Unicode: What is Unicode?. Мне понравилась, может кому будет интересна и полезна.
Юникод выглядит очень запутанным из-за чего возникает много вопросов и проблем. Многие считают, что это кодировка или набор символов(character set), что в некоторой степени правильно, но на самом деле является заблуждением. То, что Юникод изначально создавался как и кодировка и набор символов, только усиливает заблуждения. Это попытка все разъяснить не просто рассказав что такое Юникод, а предоставив модель понимания(mental model) Юникода.
Совершенно не верная, но Полезная Модель Понимания Юникода(здесь и далее ПМПЮ):
- Юникод являет собой способ обработки текстовых данных. Это не набор символов и не кодировка.
- Юникод - это текст, все остальное - бинарные данные. И даже ASCII-текст является бинарными данными.
- Юникод использует набор символов UCS. Но UCS - не Юникод.
- Юникод может быть закодирован в бинарный вид с помощью UTF. Но UTF - не Юникод.
Теперь, если вы знаете что-то о Юникод, вы скажете: "Нууу, да, но это на самом деле не так". Поэтому попытаемся выяснить почему эта модель понимания, будучи не верной, все таки полезна. Начнем с набора символов...
О наборе символов(character set)
Что бы обработать текст на компьютере, необходимо сопоставить графемы, которые вы пишете на бумаге, с номерами. Такое сопоставление определяется набором символов, или таблицей символов, которая определяет номер для символа. Вот это и называется "набором символов". Символ не обязательно соответствует какой-либо графеме. Например, существует контрольный символ "BEL", который заставляет ваш компьютер "пищать". Количество символов в наборе символов обычно 256, именно столько влазит в один байт. Есть наборы символов, которые занимают всего 6 бит. Долгое время в вычислениях доминировали 7-ми битные ASCII наборы символов, но 8-ми битные наиболее распространены.
Но 256 явно не то число, которое может вместить все необходимые в нашем мире символы. Вот поэтому и появился Юникод. Когда я сказал, что Юникод не набор символов, я соврал. Изначально Юникод был 16-ти битным набором символов. Но не смотря на то, что создатели Юникод считали, что 16 бит вполне достаточно(и они были правы), некоторые считали что 16 бит совсем не достаточно(и они тоже были правы). В конце концов они создали конкурирующую кодировку и назвали ее Универсальный набор символов(Universal Character Set, UCS). Через некоторое время команды решили объедение усилия и эти два набора символов стали одинаковыми. Вот поэтому мы можем считать, что Юникод использует UCS в качестве набора символов. Но это так же и ложь - на самом деле у каждого стандарта свой набор символов, просто так вышло, что они одинаковые(хотя UCS немного отстает).
Но для нашей ПМПЮ - "Юникод - это не набор символов".
О UCS
UCS - это 31-битный набор символов, который содержит более 100 000 символов. 31 бит - что бы не решать проблему "знаковые числа против без знаковых". Так как сейчас используется меньше чем 0.005% из всего возможного количества символов, этот лишний бит совсем не нужен.
Хотя 16 бит было не достаточно, что бы вместить все символы когда-либо созданные человечеством, этого вполне достаточно если вы готовы ограничить себя только ныне существующими языками. Поэтому основная часть символов UCS влезла в первые 65536 номеров. Их назвали "Basic Multilingual Plane" или BMP. Они по сути и есть 16-ти битный набор символов Юникода, хотя каждая версия UCS расширяет его все новыми и новыми символами. BMP становится актуальным, когда речь идет о кодировках, но про это немного ниже.
Каждый символ в UCS имеет название и номер. Символ "H" имеет название "LATIN CAPITAL LETTER H"(ЗАГЛАВНАЯ ЛАТИНСКАЯ БУКВА H) и номер 72. Номер обычно представлен в шестнадцатеричном виде, и часто с префиксом 'U+' и 4, 5 или 6 цифрами, что бы указать, что подразумевается символ Юникод. Поэтому номер символа "H" чаще представлен как U+0048, чем - 72, хотя это одно и тоже. Еще один пример, это символ "—", который называется "EM DASH", или U+2012. Символ "乨" называется "CJK UNIFIED IDEOGRAPH-4E68", чаще представлен как U+4E68. Символ "
Так как названия и номера символов в Unicode и UCS совпадают, для нашей ПМПЮ мы будем считать что UCS - не Unicode, но Unicode использует UCS. Это вранье, но полезное вранье, которое позволяет различать Юникод и набор символов.
Про кодировки
Таким образом, набор символов это совокупность символов, каждый из которых имеет свой номер. Но как вы должны хранить их, или отслать на другой компьютер? Для 8-битных символов это просто, вы используете один байт на символ. Но UCS использует 31 бит и вам необходимо 4 байта на символ, что создает проблему с порядком байтов и неэффективностью использования памяти. Так же, не все сторонние приложения могут работать со всеми символами Unicode, но нам все равно необходимо взаимодействовать с этими приложениями.
Выход - использовать кодировки, которые указывают как преобразовать Юникод текст в 8-битные бинарные данные. Примечательно то, что ASCII является кодировкой, и ASCII данные с точки зрения ПМПЮ являются бинарными!
В большинстве случаев кодировка является так же набром символов и называется так же как и набор символов, который кодирует. Это верно для Latin-1, ISO-8859-15, cp1252, ASCII и др. В то время, как большинство наборов символов являются и кодировками, для UCS это не так. Также запутывает, что UCS это то, во что вы декодируете и из чего кодируете, в то время как остальные наборы символов являются тем, из чего вы декодируете и во что кодируете(так как название кодировки и набора символов совпадает). Таким образом вы должны воспронимать набор символов и кодировки как разные вещи, не смотря на то, что часто эти термины взаимозаменяемы по смыслу.
Про UTF
Большинство кодировок работают с набором символов, которые являются лишь малой частью UCS. Это становится проблемой для многоязычных данных, вот по этому необходима кодировка, которая использует все символы UCS. Кодировка 8-битных символов очень простая, так как вы получаете один символ из одного байта, но UCS использует 31 бит и вам необходимо 4 байта на символ. Появляется проблема порядка байтов так как некоторые системы используют порядок от старшего к младшему, другие - наоборот. Так же часть байтов всегда будут пустыми, это пустая трата памяти. Правильная кодировка должная использовать различное количество байтов для различных символов, но такая кодировка будет эффективная в одних случаях и не эффективна в других.
Решение этой головоломки - использовать несколько кодировок из которых вы можете выбрать подходящую. Они называются Unicode Transformation Formats, или UTF.
UTF-8 - самая распространенная кодировка в Интернете. Она использует для ASCII символов один байт, а для всех остальных символов UCS 2 или 4 байта. Это очень эффективно для языков использующих латинские буквы, так как они все входят в ASCII, достаточно эффективно для Греческого, Кириллицы, Японского, Армянского, Сирийского, Арабского и др., так как для них используется 2 байта на символ. Но это не эффективно для всех остальных языков из BMP, так как будет использоваться 3 байта на символ, а для всех остальных символов UCS, например Готическое письмо, будет использоваться 4 байта.
UTF-16 использует для всех символов из BMP одно 16-битное слово и два 16-битных слова для всех остальных символов. По этому, если вы не работает содним из упомянутых выше языков, вам лучше использовать UTF-16. Так как UTF-16 использует 16-битные слова, мы получаем проблему порядка байтов. Она решена наличием трех вариантов: UTF-16BE для порядка байтов от старшего к младшему, UTF-16LE - от младшего к старшему, и просто UTF-16, который может быть UTF-16BE или UTF-16LE, при кодировании в начале используется маркер, который указывает порядок байтов. Этот маркер называется "byte order mark", или "BOM".
Так же существует UTF-32, который может быть в двух вариантах BE и LE как и UTF-16, и хранит символ Юникод как 32-битное целое число. Это не эффективно почти для всех символов, кроме тех, которые требуют 4 байта для хранения. Но при этом очень легко обрабатывать такие данные, так как у вас всегда 4 байта на символ.
Важно разделять кодированные данные от данных Юникод. По этому не думайте о UTF-8/16/32 данных как о Юникод. Таким образом хотя кодировки UTF и определены в стандарте Unicode, в рамках ПМПЮ мы считаем что UTF - это не Юникод.
О Юникод
UCS содержит объединяющие символы, например трема, которая добавляет две точки над символом. Это ведет к неопределенности при выражении одной графемы(буква или знак) через несколько символов. Возьмем для примера ‘ö’, который может быть представлен как символ LATIN SMALL LETTER O WITH DIAERESIS, но в тоже время как сочетание символов LATIN SMALL LETTER O и следующим за ним COMBINING DIAERESIS.
Но в реальной жизни вы не можете дополнить любой символ тремой. Например, нет смысла добавлять две точки над символом Евро. Юникод содержим правила для таких вещей. Он указывает что вы можете выразить ‘ö’ двумя способами и это будет один и тот же символ, но если вы используете трему для знака Евро, вы совершаете ошибку. Таки образом правила комбинирования символов являются частью стандарта Юникод.
Стандарт Юникод содержит так же правила для сравнения символов и сортировки, правила разбивки текста на предложения и слова(если вы думаете это так просто, учтите что большинство Азиатских языков не содержат пробелы между слов), и много других правил, который определяют как отображается и обрабатывается текст. Скорее всего вам и не понядобится все это знать, разве что при использовании Азиатских языков.
Используя ПМПЮ мы определили, что Юникод это UCS плюс правила обработки текста. Или другими словами: Юникод это способ работы с текстовыми данными и не важной какой язык или письмо они используют. В Юникод ‘H’ это не просто символ, он имеет какое-то смысл. Набор символов указывает что ‘H’ символ с номером 72, в то время как Юникод говорит вам, что при сортировке ‘H’ следует перед ‘I’ и вы можете использовать две точки над ним получив ‘Ḧ’.
Таким образом Юникод - это не кодировка и не набор символов, это способ работы с текстовыми данными.