Как стать автором
Обновить

Что такое Unicode? Или как компьютеры работают с символами

Время на прочтение 7 мин
Количество просмотров 10K

Основная задача письменности с давних времен, отобразить визуально то, что человек произносит вербально. В истории встречается огромное количество примеров того, как люди, пытаясь передать через бумагу какую-то информацию, использовали для этого знакомые образы. Древние египтяне использовали иероглифы, очень похожие на вещи из повседневной жизни. 

Люди перешли от изображения быка (𓃾) к финикийской нотации (𐤀), которая со временем стала ассоциироваться с произношением слова (‘alp), как в случае с греческой альфой (α), пока она не стала латинской заглавной буквой (А).

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

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

ASCII

Мы знаем что память компьютера представлена в виде нулей и единиц. Как в таком случае, он может понять, что такое символ, и какие именно символы нужны, чтобы отобразить какой-то текст?

Чтобы решить эту проблему, программисты придумали таблицу символов ASCII (American standard code for information interchange)

Во времена изобретения UNIX системы, символы выделяли лишь 8 бит памяти. В те дни использование памяти имело большое значение, потому что она у компьютеров того времени была в дефиците. Написание чисел в виде байт-кода, не самая удобная задача для программиста. ASCII решал эту проблему и по своей сути, представляет таблицу поиска байтов в символы. Каждый символ этой таблицы относится к своему номеру, и может быть представлен в виде двоичного числа.

01100010 01101001 01110100 01110011
b        i        t        s

В этой ASCII таблице записано всего 95 буквенно-цифровых символа, чего вполне достаточно, если вы говорите по-английски. Для экономии места, ASCII использует для отображения английского языка всего 7 бит (0-127) вместо 8, чего вполне достаточно для этих 95 символов.

Но что же делать с другими языками? Как отобразить их?

Для решения этой проблемы, программисты из разных стран, решили, что можно использовать незанятое место в ASCII байте (128-255) и использовать свободное место для символов из других языков. Некоторые языки пошли дальше, и для кодировки текста стали использовать 16 бит, если 8 бит было недостаточно.
К концу 90х было примерно 60 разных стандартов кодировки.

Пример ASCII таблицы с символами русского языка
Пример ASCII таблицы с символами русского языка

И действительно для большинства языков мира это сработало. Однако до того момента пока этот файлы оставались локально на компьютере (да-да, интернет был не всегда)

Сами по себе документы очень редко обозначали свою кодировку. Когда люди начали обмениваться файлами, то на другом конце, получатель мог просто не знать о том формате, которым пользовался отправитель и в попытке прочитать полученный файл, получал очень странный результат:

�����[ Éf����Õì ÔǵÇ���¢!!

WEB

Чтобы как-то справится с этой проблемой был добавлен специальный HTML тег, позволяющий указывать определенную кодировку. Если этого тега не было, клиент мог получить кодировку в ответ от сервера. Однако самое веселье начиналось тогда, когда все эти значения были разные. Поэтому в старых версиях Google Chrome можно было вручную изменить тип кодировки.

DNS

Появилась проблема в доменных именах. DNS сервера понимали только первые 7 бит ASCII кодировки, что сильно ограничивало пространство доменных имен. Для решения этой проблемы даже был придуман Punycode.  Который позволял кодировать 8/16/32 битные символы, используя значения из ASCII таблицы. 

Пример

Например доменное имя: мойлюбимыйсайт.ру в Punycode кодировке выглядел бы вот так: xn--80abvccbjgep3bo6iqb.xn--p1ag:

MAIL

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

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

Unicode

Unicode - стандарт для обработки, кодировки и представления текста во всем мире.
Юникод хранит свои значения не в глифах, а в графемах. Каждая графема имеет свой Code Unit. Code Unit - в общем понимании, это уникальный номер, который присваивается каждой графеме. Обычно Code Point записывается в виде U+ и затем идут 4-6
шестнадцатеричные цифры. Например, Code Unit символа заглавной латинской буквы A это - U+0041

Вместо того чтобы попытаться закодировать все возможные комбинации, Unicode предоставляет динамическую композицию.
Например, символ é может быть представлен как единственный символ U+00E9 (É) , либо в виде комбинации U+0065 (E) вместе с U+0301 (◌́) . Для динамической композиции используются так называемые суррогаты - спец символы которые используются для комбинации с другими символами.
Все это сделано для того, чтобы юникод был максимально гибким и мог собрать текст как по кирпичикам, отвечая только за его логическую суть. Вся ответственность за визуальную часть: размер, форма, шрифт, стиль, ложится на плечи устройства, которое декодирует Unicode для последующего отображения.

Пример UNICODE
Пример UNICODE

Еще один плюс Unicode, так это то, что он был придуман с сохранением обратной совместимости с ASCII и все сайты которые использовали ASCII не пострадали. (Остальным пришлось адаптироваться)

Секторы Unicode

Юникод определяет кодовое пространство размером от U+0000 до U+10FFFF. Что равно примерно 1,114,112 символам которые поделены на 17 отдельных секторов. Каждый сектор хранит 2¹⁶ или 65,536 символов.

Сектор 0 (U+0000 –U+FFFF): Базовый мультиязычный сектор
Содержит самые часто используемые символы, в том числе латинские, кириллицу, арамейские, брахмические, и другие различные символы.
Сектор 1 (U+10000– U+1FFFF): Дополнительным многоязычный сектор Содержит исторические символы, такие как египетские иероглифы, изобразительные символы, как ноты и игральные карты, а также смайлики.
Сектор 2 (U+20000- U+2FFFF): Дополнительным идеографический сектор
Содержит китайские, японские и корейские иероглифы, которые не были добавлены как часть более ранних стандартов.
Сектора с 3 по 13 (U+30000 — U+DFFFF):
В настоящее время пусты.
Сектор 14 (U+E0000 - U+EFFFF): Дополнительный сектор специального назначения.
В частности, здесь содержат символы региональных индикаторов, используемые для формирования флага страны, например, в эмодзи.
Сектора 15 и 16 (U+F0000 - U+10FFFF):
Обозначены как области частного использования, а так же в этой области хранятся символы суррогаты о которых говорилось ранее.

UTF-8/UTF-16/UTF-32

Вместе с Unicode идет несколько механизмов для кодирования данных, такие как UTF-8, UTF-16, UTF-32.

  • UTF-8 диапазон 1 до 4 байт

  • UTF-16 диапазон 2 или 4 байт.

  • UTF-32 фиксированный размер 4 байта.

UTF-8

UTF-8 хранит каждый символ Unicode, используя от 1 до 4
8-битных единицы. В таблице снизу показано, как диапазоны скалярных
значения представлены в UTF-8:

UTF-16

UTF-16 хранит каждый символ Unicode в 1 или 2 16-битных единицах. В следующей таблице показано, как распределяются диапазоны скалярных значений в UTF-16:

UTF-32

UTF-32 представляет каждый символ Unicode в одном 32-битной единице. В следующей ниже показано, как распределяются диапазоны скалярных значений в UTF-32:

UTF-8 и UTF-16 имеют динамическую длину, в то время когда UTF-32 всегда имеет фиксированный размер. UTF-8 Сохраняет очень много места. Если большинство текста написано на английском языке, и может поместиться в размер 1 байт, то UTF-8 более предпочтительный выбор, так как может сократить объем передаваемых данных в 4 раза если сравнивать с UTF-32.

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

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

Исходя из примера выше, можно сделать вывод: когда нам нужно использовать более гибкий и экономичный вид кодировки для передачи информации - UTF-8/UTF-16 отлично для этого подойдут. Однако иногда, если мы хотим точно быть уверены, что каждый наш символ занимает фиксированный размер - тогда лучше использовать UTF-32.

На этом моменте могло сложиться что Unicode идеальное решение. Однако это не так. У него тоже есть проблемы. В зависимости от операционной системы и языка программирования, Unicode обрабатывается по-разному, и в некоторых случаях, длина строки, либо количество символов в ней может отличаться. Тут и начинается самое веселье, которое выпало на долю разработчиков. Unicode ещё не завершён. Как и в случае с любым стандартом, мы что-то добавляем, убираем, предлагаем новое. Никакие спецификации нельзя назвать «завершёнными». Обычно в год бывает 1-2 релиза

Например в Unicode буквы "E" и "É", как говорилось ранее, можно записывать по-разному - одним символом и двумя символами "E" + глиф. Разница в том что: 

  • У символов будет размер разный.

  • При неправильной кодировке двойной клик на слове с такими комбинированным "É" выделяет слово не полностью.

  • Может некорректно отрабатывать сортировка.

На этом примере мы можем догадаться, что не все программы и сайты умеют правильно приводить Code Points к такому виду, который позволяет сравнивать одинаковые глифы, записанные с использованием разных Code Points. Иными словами, не каждая программа и сайт распознает «É» и «É» за один символ, из-за чего становится невозможно, например, производить поиск по таким буквам.

TL;DR

Юникод — это общий массив символов для всех языков мира, глифов и эмодзи. Семейство кодировок UTF — это то, как компьютеры узнают, какая последовательность битов должна быть представлена чтобы получить нужный символ. Однако каждый язык программирования, приложение и ОС реализуют и поддерживают Unicode по-разному (если вообще используют). Вот где начинается веселая жизнь разработчика 😬.

Теги:
Хабы:
+9
Комментарии 6
Комментарии Комментарии 6

Публикации

Истории

Работа

Data Scientist
75 вакансий
Java разработчик
401 вакансия
Swift разработчик
47 вакансий
iOS разработчик
32 вакансии
Python разработчик
148 вакансий
PHP программист
136 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн