EngineerSpock

Абсолютный минимум, который должен знать каждый разработчик программного обеспечения о Юникоде в 2023 году (и никаких оправданий!)

Переводы: Французский Китайский

Двадцать лет назад Джоэл Спольски писал:

Не существует такой вещи, как простой текст.

Нет никакого смысла в строке, если не знаешь, какую кодировку она использует. Вы больше не можете прятать голову в песок и притворяться, что «простой» текст — это ASCII.

За 20 лет многое изменилось. В 2003 году главным вопросом было: что это за кодировка?

В 2023 году такой вопрос уже не стоит: с вероятностью 98 % это UTF-8. Наконец-то! Снова можно засунуть голову в песок!

Supplementary planes

Дополнительные плоскости

Joel’s article

Статья Джоэла

Extended grapheme clusters

Расширенный кластер графем

Emoji

Эмодзи

Yearly major revisions

Ежегодные главные версии

Web pages that use UTF-8

Веб страницы, которые используют UTF-8

Теперь возникает вопрос: как правильно использовать UTF-8? А давайте посмотрим!

Что такое Юникод?

Юникод — это стандарт, целью которого является унификация всех человеческих языков, как современных, так и тех, которые существовали ранее, и обеспечение их совместимости с компьютерными системами.

На практике Юникод представляет собой таблицу, в которой разным символам присваиваются уникальные номера.

Например.

  • Латинской букве A присвоено число 65.
  • Арабской букве Seen س присвоено число 1587.
  • Иероглифу азбуки катакана Tu ツ присвоено число 12484
  • Музыкальному символу G Clef 𝄞 присвоено число 119070.
  • 𝄞 присвоено число 128169.

Юникод называет эти числа кодовыми точками.

Поскольку в мире согласовано соответствие между символами и числами, и мы все согласны использовать Юникод, мы можем читать тексты друг друга.

Юникод == символ ⟷ кодовая точка.

Лайкосы / Подписки / Курсы

Насколько велик Юникод?

В настоящее время самая большая определенная кодовая точка — 0x10FFFF. Это область примерно в 1,1 миллиона кодовых точек.

В настоящее время определены около 170 000, или 15 %. Еще 11 % зарезервировано для частного использования. Остальные, около 800 000 кодовых точек, на данный момент не выделены. Они могут стать символами в будущем.

Вот примерно, как это выглядит:

Latin

Латиница

Symbols

Символы

Tangut

Тангутский язык

Yi hangul

Корейская азбука

Surrogates

Суррогатные пары

Emoji

Эмодзи

Not allocated

Не выделено

Weird emoji

Странные эмодзи

Private use

Частное использование

Большой квадрат == плоскость == 65 536 символов. Маленький квадрат == 256 символов. Весь ASCII представляет собой половину небольшого красного квадрата в верхнем левом углу.

Что такое частное использование?

Это кодовые точки, зарезервированные для разработчиков приложений, и они никогда не будут определены самим Юникодом.

Например, логотипу Apple нет места в Юникоде, поэтому Apple помещает его в U+F8FF который находится в блоке частного использования. В любом другом шрифте он будет отображаться как отсутствующий глиф 􀣺, но в шрифтах, поставляемых с macOS, вы увидите .

Область частного использования в основном используется иконочными шрифтами:

Разве это не красота? Это все текст!

Что означает U+1F4A9?

Это соглашение о том, как записывать значения кодовых точек. Префикс U+ используется для обозначения Юникода, а 1F4A9 — это шестнадцатеричный номер кодовой точки.

Да, и конкретно +1F4A9 это 𝄞.

А что тогда такое UTF-8?

UTF-8 — это кодировка. Кодирование — это способ сохранения кодовых точек в памяти.

Самая простая возможная кодировка Юникода — UTF-32. Это простое сохранение кодовых точек как 32-битных целых чисел. Таким образом, U+1F4A9 становится 00 01 F4 A9, занимая четыре байта. Любая другая кодовая точка в UTF-32 также будет занимать четыре байта. Поскольку наивысшая определенная кодовая точка — U+10FFFF, любая кодовая точка гарантированно подойдет.

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

Кодирование — это то, с чем вы на самом деле будете иметь дело как программист.

Сколько байтов в UTF-8?

UTF-8 — это кодирование с переменной длиной. Кодовая точка может быть закодирована как последовательность от одного до четырех байтов.

Вот как это работает:

Кодовая точка

Байт 1

Байт 2

Байт 3

Байт 4

U+0000..007F

0xxxxxxx

     

U+0080..07FF

110xxxxx

10xxxxxx

   

U+0800..FFFF

1110xxxx

10xxxxxx

10xxxxxx

 

U+10000..10FFFF

11110xxx

10xxxxxx

10xxxxxx

10xxxxxx

Если вы объедините это с таблицей Юникода, вы увидите, что английский язык кодируется 1 байтом, для кодирования кириллицы, европейских языков, где используется латиница, иврита и арабского языка требуют 2 байта, а для кодирования китайского, японского, корейского, других азиатских языков и эмодзи требуют 3 или 4 байта.

Здесь несколько важных моментов.

Во-первых, UTF-8 совместим по байтам с ASCII. Кодовые точки 0..127, предыдущий ASCII, кодируются одним байтом, и это один и тот же байт. U+0041 (A, латинская заглавная буква A) — это всего лишь 41, один байт.

Любой чистый текст ASCII также является корректным текстом UTF-8, и любой текст UTF-8, который использует только кодовые точки 0–127, может быть прочитан напрямую как ASCII.

Во-вторых, UTF-8 — компактный (занимает мало места) для базовой латиницы. Это было одним из главных преимуществ UTF-16. Возможно, это несправедливо по отношению к текстам из других стран мира, но для технических строк, таких как теги HTML или ключи JSON, использовать его представляется вполне целесообразным.

В целом UTF-8 неплохо подходит даже для компьютерных систем, где не используется английский язык. А для английского языка он вообще идеален.

В-третьих, UTF-8 имеет встроенные функции обнаружения и устранения ошибок. Префикс первого байта всегда выглядит иначе, чем байты 2–4. Таким образом, вы всегда можете определить, смотрите ли вы на полную и действительную последовательность байтов UTF-8 или чего-то не хватает (например, вы перескочили середину последовательности). Затем вы можете исправить это, двигаясь вперед или назад, пока не найдете начало правильной последовательности.

И пара важных следствий.

  • Вы НЕ МОЖЕТЕ определить длину строки путем подсчета байтов.
  • Вы НЕ МОЖЕТЕ случайно перепрыгнуть в середину строки и начать читать.
  • Вы НЕ МОЖЕТЕ получить подстроку, разрезая в произвольных байтовых смещениях. Вы можете отрезать часть символа.

И те, кто так сделает, в конечном итоге столкнуться с этой штукой: �

Что такое �?

U+FFFD, символ замены, — это просто еще одна кодовая точка в таблице Юникода. Приложения и библиотеки могут использовать его при обнаружении ошибок Юникода.

Если вы отрежете половину кодовой точки, с другой половиной ничего не останется сделать, кроме как отобразить ошибку. Именно тогда используется �.

var bytes = “Аналитика”.getBytes(“UTF-8”);

var partial = Arrays.copyOfRange(bytes, 0, 11);

new String(partial, “UTF-8”); // => “Анал�”

Разве не будет проще использовать UTF-32 во всем?

НЕТ.

UTF-32 отлично подходит для работы с кодовыми точками. Действительно, если каждая кодовая точка всегда имеет длину 4 байта, то strlen(s) == sizeof(s) / 4, substring(0, 3) == bytes[0, 12], и т. д.

Проблема в том, что вы не хотите работать с кодовыми точками. Кодовая точка не является единицей записи; одна кодовая точка не всегда является одним символом. То, с чем вам придется работать, называется «расширенными кластерами графем» или сокращенно графемами.

Графема — минимальная установленная единица записи в контексте конкретной системы записи. ö — одна графема, и é — тоже одна из них. И 각. По сути, графема — это то, что пользователь воспринимает как отдельный символ.

Проблема в том, что в Юникоде некоторые графемы закодированы несколькими кодовыми точками!

Bytes

Байты

Code points

Кодовые точки

Glyphs

Глифы

Graphemes

Графемы

Например, é (одна графема) кодируется в Юникоде как e (U+0065 латинская строчная буква E) + ´ (U+0301 комбинированный диакритический знак ΄). Две кодовых точки!

Их также может быть больше двух:

  • ☹️ — U+2639 + U+FE0F

  • 👨🏭 — U+1F468 + U+200D + U+1F3ED

  • 🚵🏻‍♀️ — U+1F6B5 + U+1F3FB + U+200D + U+2640 + U+FE0F

  • y̖̠͍̘͇͗̏̽̎͞ — U+0079 + U+0316 + U+0320 + U+034D + U+0318 + U+0347 + U+0357 + U+030F + U+033D + U+030E + U+035E

Насколько я знаю, ограничений нет.

Помните, здесь мы говорим о кодовых точках. Даже в самой широкой кодировке UTF-32 для кодирования ‍ все равно потребуется три 4-байтовых блока. И его все равно нужно рассматривать как один символ.

Если аналогия помогает, мы можем думать о самом Юникоде (без каких-либо кодировок) как о коде переменной длины.

Кластер расширенных графем — это последовательность одной или нескольких кодовых точек Юникода, которую следует рассматривать как один неразрывный символ.

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

Нарушение кластеров графем приводит к таким ошибкам:

Просто чтобы внести ясность: это НЕПРАВИЛЬНО.

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

Кодовые точки — 🥱. Графемы — 😍

Юникод сложен только из-за эмодзи?

Не совсем. Кластеры расширенных графем используются также и для живых, активно используемых языков. Например.

  • ö (немецкий) — это один символ, но несколько кодовых точек (U+006F U+0308).
  • ą́ (литовский) — U+00E1 U+0328.
  • 각 (корейский) — U+1100 U+1161 U+11A8.

Так что нет, дело не только в эмодзи.

Что такое длина “‍🤦🏼‍♂️“.length?

Вопрос навеян этой блестящей статьей.

Разные языки программирования с радостью дадут вам разные ответы.

Python 3:

>>> len(“‍🤦🏼‍♂️”)

5

JavaScript / Java / C#:

>> “‍🤦🏼‍♂️”.length

7

Rust:

println!(“{}”, “‍🤦🏼‍♂️”.len());

// => 17

Как вы можете догадаться, разные языки используют разные внутренние представления строк (UTF-32, UTF-16, UTF-8) и сообщают длину в любых единицах хранения символов (целые числа, короткие числа, байты).

НО! Если вы спросите любого нормального человека, не погруженного в мир внутренних устройств компьютера, он вам даст прямой ответ: 1. Длина строки ‍🤦🏼‍♂️ — 1.

В этом и суть расширенных кластеров графем: то, что люди их воспринимают как один символ. И в данном случае ‍🤦🏼‍♂️, несомненно, является одним символом.

Тот факт, что ‍🤦🏼‍♂️ состоит из 5 кодовых точек (U+1F926 U+1F3FB U+200D U+2642 U+FE0F), является всего лишь элементом реализации. Он не должен разбиваться на части, не должен считаться за несколько символов, внутри него не должен помещаться текстовый курсор, он не должен выделяться частично и т. д.

По сути, это неделимая единица текста. Внутри он может быть закодирован, как угодно, но для внешнего пользовательского API-интерфейса, его следует рассматривать как единое целое.

Единственный современный язык, который понимает это правильно, — это Swift:

print(“‍🤦🏼‍♂️”.count)

// => 1

По сути, существует два слоя.

  1. Внутренний, машинно-ориентированный. Как копировать строки, отправлять их по сети, сохранять на диске и т. д. Вот здесь и нужны кодировки типа UTF-8. Swift для внутренних целей использует UTF-8, но с тем же успехом это может быть UTF-16 или UTF-32. Важно то, что вы используете его только для копирования строк целиком и никогда для анализа их содержимого.
  2. Внешний человеко-ориентированный API-интерфейс. Подсчет символов в пользовательском интерфейсе. Берем первые 10 символов для создания предварительного просмотра. Ищем в тексте. Такие методы, как .count или .substring. Swift дают вам представление, в котором строка представляет собой последовательность кластеров графем. И это представление соответствует ожиданиям любого человека: для “‍🤦🏼‍♂️”.count — 1.

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

Вопрос к читателю: какой, по вашему мнению, должна быть длина ?

Как мне тогда обнаружить расширенные кластеры графем?

К сожалению, большинство языков выбирают простой путь и позволяют вам перебирать строки размером 1-2-4 байта, но не кластеры графем.

Это не имеет смысла и никакой семантики, но, поскольку это значение по умолчанию, программисты не сильно задумываются, и в результате мы видим искаженные строки:

«Я знаю, я буду пользоваться библиотекой для выполнения strlen()!» — никто, никогда.

Но это именно то, что вам следует делать! Используйте подходящую библиотеку Юникода! Да, для таких элементарных вещей, как strlen или indexOf или substring!

Например.

  1. C/C++/Java: использует ICU. Это библиотека самого Юникода, которая кодирует все правила сегментации текста.
  2. C#: используйте TextElementEnumerator, который, насколько я могу судить, поддерживается в актуальном состоянии Юникодом.
  3. Swift: просто стандартная библиотека stdlib. Swift по умолчанию делает правильные вещи.
  4. UPD: Erlang/Elixir, похоже, тоже делают то, что надо.
  5. Для других языков, вероятно, имеется библиотека или привязка для ICU (Международные компоненты для Юникода).
  6. Создайте свою собственную библиотеку. Юникод публикует правила и таблицы в машиночитаемом формате, и все вышеперечисленные библиотеки основаны на них.

Но что бы вы ни выбрали, убедитесь, что это самая последняя версия Юникода (15.1 на момент написания статьи), потому что определение графем меняется в зависимости от версии. Например, Java’s java.text.BreakIterator не подходит: он основан на очень старой версии Юникода и не обновляется.

Используйте библиотеку.

ИМХО, вся эта ситуация — позор. Юникод должен быть в стандартной библиотеке каждого языка по умолчанию. Это общепринятый язык интернета! Это даже не какая-то новинка: мы живем с Юникодом уже 20 лет.

Подождите, правила меняются?

Да! Разве это не круто?

(Я знаю, это не так.)

Примерно начиная с 2014 года Юникод ежегодно выпускает главную версию своего стандарта. Здесь вы найдете новые эмодзи — осенние обновления Android и iOS обычно включают, среди прочего, новейший стандарт Юникод.

Unicode release dates

Даты выпуска Юникода

Version

Версия

Year

Год

Month (day)

Месяц (день)

September

Сентябрь

March

Март

May

Май

June

Июнь

January

Январь

October

Октябрь

April

Апрель

July

Июль

Что для нас печально, так это то, что правила определения кластеров графем также меняются каждый год. То, что сегодня считается последовательностью из двух или трех отдельных кодовых точек, завтра может стать кластером графем! Нет никакой возможности узнать! Или готовьтесь!

Хуже того, разные версии вашего приложения могут работать в разных стандартах Юникода и сообщать о разной длине строк!

Но такова реальность, в которой мы живем. Здесь у вас нет выбора. Вы не можете игнорировать Юникод или обновления Юникода, если хотите оставаться в игре и обеспечивать качество обслуживания своих клиентов по высшему разряду. Итак, пристегнитесь, распахните свои объятья и обновитесь.

Обновляйтесь ежегодно.

Почему “Å” !== “Å” !== “Å”?

Скопируйте любую из этих строчек в консоль JavaScript:

“Å” === “Å”

“Å” === “Å”

“Å” === “Å”

Что вы получаете? Значение «False»? Вы должны получить значение «false», и это не ошибка.

Помните, ранее я говорил, что ö — это две кодовых точки, U+006F U+0308? По сути, Юникод предлагает несколько способов написания таких символов, как ö или Å. Вы можете:

  1. Образовать Å из обычной латиницы A + комбинированный символ,
  2. ИЛИ имеется заранее составленный код U+00C5, который сделает это за вас.

Они будут выглядеть одинаково (Å и Å), они должны работать одинаково, и фактически они считаются абсолютно одинаковыми. Единственное отличие — это представление байта.

Вот почему нам необходима нормализация. Существует четыре формы:

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

NFC, с другой стороны, пытается объединить все в заранее составленную форму, если таковая существует.

Source

Источник

NFD – canonical decomposition

NFD – каноническое разложение

NFC – canonical composition

NFC – каноническое сложение

Для некоторых символов в Юникоде также существует несколько версий. Например, есть U+00C5 латинская заглавная буква с кольцом сверху, но также есть и U+212B знак ангстрема, который выглядит точно так же.

Они также заменяются во время нормализации:

Source

Источник

NFD – canonical decomposition

NFD – каноническое разложение

NFC – canonical composition

NFC – каноническое сложение

NFD и NFC называются «канонической нормализацией». Есть еще две формы — «нормализация совместимости»:

NFKD пытается все разбить на составные части и заменяет визуальные варианты вариантами по умолчанию.

NFKC пытается объединить все, заменяя визуальные варианты вариантами по умолчанию.

Source

Источник

NFKD – compatibility decomposition

NFKD – разложение по совместимости

NFKC – compatibility composition

NFKC –сложение по совместимости

Визуальные варианты — это отдельные кодовые точки Юникода, которые представляют один и тот же символ, но должны отображаться по-разному. Например, ① или ⁹ или 𝕏. Мы хотим иметь возможность находить и “x”, и “2” в строке типа ” 𝕏²”, не так ли?

Related characters

Связанные символы

Все они имеют свои собственные кодовые точки, но это все также и символ X.

Почему у лигатуры fi вообще имеется свой код? Без понятия. В миллионе символов можно найти всякое.

Прежде чем сравнивать строки или искать подстроку, выполняйте нормализацию!

Юникод зависит от локали

Русское имя Николай пишется так:

а в Юникоде кодируется как U+041D 0438 043A 043E 043B 0430 0439.

Болгарское имя Николай пишется так:

а в Юникоде кодируется как U+041D 0438 043A 043E 043B 0430 0439. Точно так же!

Одну секунду! Как компьютер узнает, когда отображать болгарские глифы, а когда использовать русские?

Короткий ответ: никак. К сожалению, Юникод не является идеальной системой и имеет множество недостатков. Среди них — присвоение одной и той же кодовой точки глифам, которые должны выглядеть по-разному, например, кириллической строчной букве K и болгарской строчной букве K (обе закодированы как U+043A).

Насколько я понимаю, азиатам приходится гораздо хуже: многим китайским, японским и корейским логограммам, написанным совершенно по-разному, присваивается один и тот же код:

Simplified Chinese

Упрощенный китайский

Traditional Chinese – Taiwan

Традиционный китайский – Тайвань

Traditional Chinese – Hong Kong

Традиционный китайский – Гонконг

Japanese

Японский

Korean

Корейский

U+8FD4 в разных локалях

Мотивацией Юникода является экономия места в кодовых точках (на мой взгляд). Предполагается, что информация о способе представления передается за пределы строки в виде метаданных локали/языка.

К сожалению, это не соответствует первоначальной цели Юникода:

[…] для указания любого символа в любом языке не требуется escape-последовательность или управляющий код.

На практике зависимость от локали приносит массу проблем.

  1. Так как локаль — это метаданные, она часто теряется.
  2. Люди не ограничены одной локалью. Например, я могу читать и писать на английском (США), английском (Великобритания), немецком и русском языках. Какую локаль мне следует установить на своем компьютере?
  3. Трудно смешивать и сочетать. Как русские имена в болгарском тексте или наоборот. Почему нет? Это интернет, здесь тусуются люди всех культур.
  4. Нет места для задания локали. Даже создание двух скриншотов, приведенных выше, было нетривиальной задачей, поскольку в большинстве программ нет раскрывающегося списка или ввода текста для изменения локали.
  5. При необходимости нужно было угадывать. Например, Twitter пытается угадать локаль по тексту самого твита (потому что откуда еще он мог ее взять?) и иногда ошибается:

Почему String::toLowerCase() принимает локаль в качестве аргумента?

Еще одним неудачным примером зависимости от локали является обработка в Юникоде символа i без точки в турецком языке.

В отличие от английского, в турецком языке имеется два варианта I: с точкой и без точки. Юникод решил повторно использовать I и i из ASCII и добавить только две новые кодовые точки: İ и ı.

К сожалению, это привело к тому, что toLowerCase/toUpperCase стали вести себя по-разному при одном и том же вводе:

var en_US = Locale.of(“en”, “US”);

var tr = Locale.of(“tr”);

 

“I”.toLowerCase(en_US); // => “i”

“I”.toLowerCase(tr);    // => “ı”

 

“i”.toUpperCase(en_US); // => “I”

“i”.toUpperCase(tr);    // => “İ”

Так что нет, вы не можете преобразовать строку в нижний регистр, не зная, на каком языке она написана.

Я живу в США/Великобритании, должно ли меня это волновать?

Still – yes. Even pure English text uses lots of “typographical signs” that aren’t available in ASCII, like:

Все равно — да. Даже в тексте, написанном на чистом английском языке, используется множество «типографских знаков», недоступных в ASCII, например:

  • кавычки “ ” ‘ ’,
  • апостроф ’,
  • тире – —,
  • разные варианты пробелов (фигура, очень малое расстояние, неразрывный пробел),
  • маркеры списка• ■ ☞,
  • символы валют, кроме $ (что-то вроде того, кто изобрел компьютеры, не так ли?): € ¢ £,
  • математические знаки — плюс + и равно = являются частью ASCII, а минус – и умножение × не являются ¯\_(ツ)_/¯,
  • прочие знаки © ™ ¶ † §.

Черт, да вы даже не сможете написать café, piñata или naïve без Юникода. Так что да, мы все в этом участвуем, даже американцы.

Тушэ.

Что такое суррогатные пары?

Это уходит своими корнями в Юникод v1. Первая версия Юникода должна была иметь фиксированную ширину. 16-битную фиксированную ширину, если уж быть точным:

Версия 1.0 стандарта Юникод, октябрь 1991 г.

Предполагалось, что 65 536 символов будет достаточно для всех человеческих языков. Они были почти правы!

Когда они поняли, что им нужно больше кодовых точек, во многих системах уже использовался UCS-2 (исходная версия UTF-16 без суррогатов). 16 бит, фиксированная ширина, всего 65 536 символов. Что вы можете сделать?

Юникод решил выделить некоторые из этих 65 536 символов для кодирования более высоких кодовых точек, по сути, преобразуя UCS-2 фиксированной ширины в UTF-16 переменной ширины.

Суррогатная пара — это две единицы UTF-16, используемые для кодирования одной кодовой точки Юникода. Например, D83D DCA9 (два 16-битных блока) кодирует одну кодовую точку, U+1F4A9.

Старшие 6 бит суррогатных пар используются для маски, оставляя 2×10 свободных битов:

   Старший суррогат           Младший суррогат

        D800        ++          DC00

1101 10?? ???? ???? ++ 1101 11?? ???? ????

Технически обе половины суррогатной пары также можно рассматривать как кодовые точки Юникода. На практике весь диапазон от U+D800 до U+DFFF выделяется как «только для суррогатных пар». Кодовые точки оттуда даже не считаются допустимыми ни в каких других кодировках.

Latin script

Латинский алфавит

Non-Latin European scripts

Алфавиты европейских языков, где не используется латиница

African scripts

Алфавиты африканских языков

Middle Eastern and Southwest Asian scripts

Алфавиты языков Ближнего Востока и Юго-Западной Азии

South and Central Asian scripts

Алфавиты языков Южной и Центральной Азии

Southeast Asian scripts

Алфавиты языков Юго-Восточной Азии

East Asian scripts

Алфавиты языков Восточной Азии

CJK characters

Китайские, японские, корейские иероглифы

Indonesian and Oceanic scripts

Алфавиты индонезийских и океанийских языков

American scripts

Американский алфавит

Notational systems

Системы обозначений

Symbols

Символы

Private use

Частное использование

UTF-16 surrogates

Суррогаты UTF-16

As of Unicode 15.1

Начиная с Юникод 15.1

Это пространство в очень переполненной основной многоязычной плоскости никогда больше не будет использовано ни для чего хорошего.

UTF-16 еще жив?

Да!

Обещание предоставить кодировку фиксированной ширины, охватывающей все человеческие языки, было настолько убедительным, что многие системы стремились к использованию такой кодировки. Среди них были Microsoft Windows, Objective-C, Java, JavaScript, .NET, Python 2, QT, SMS и CD-ROM!

С того времени многое изменилось: Python продолжает жить, CD-ROM устарел, но все остальное осталось в UTF-16 или даже UCS-2. Таким образом, UTF-16 существует как представление в памяти.

С практической точки зрения сегодня UTF-16 имеет примерно такое же удобство использования, как и UTF-8. Это все то же кодирование с переменной длиной; подсчет единиц UTF-16 так же бесполезен, как и подсчет байтов или кодовых точек, кластеры графем по-прежнему вызывают боль и т. д. Единственное различие — это требования к памяти.

Единственным минусом UTF-16 является то, что все остальное закодировано в UTF-8, поэтому каждый раз, когда строка считывается из сети или с диска, требуется преобразование.

Кроме того, интересный факт: количество плоскостей в Юникоде (17) определяется тем, сколько вы можете выразить с помощью суррогатных пар в UTF-16.

Заключение

Подведем итог:

  • Юникод победил.
  • UTF-8 — самое популярное кодирование для передаваемых и хранящихся данных.
  • UTF-16 до сих пор иногда используется в качестве представления в памяти.
  • Двумя наиболее важными представлениями для строк являются байты (выделение памяти/копирование/кодирование/декодирование) и расширенные кластеры графем (все семантические операции).
  • Использование кодовых точек для перебора строки некорректно. Они не являются основной единицей записи. Одна графема может состоять из нескольких кодовых точек.
  • Чтобы определить границы графем, вам нужны таблицы Юникода.
  • Используйте библиотеку Юникода для всего Юникода, даже для таких скучных вещей, как strlen, indexOf и substring.
  • Юникод обновляется каждый год, и правила иногда меняются.
  • Строки Юникода необходимо нормализовать, прежде чем их можно будет сравнивать.
  • Юникод зависит от локали для выполнения некоторых операций и отображения информации.
  • Все это важно даже для чисто английского текста.

В целом да, Юникод не идеален, но тот факт, что

  1. существует кодирование, охватывающее все возможные языки одновременно,
  2. весь мир согласен использовать его,
  3. мы можем полностью забыть о кодировках, преобразованиях и всем таком прочем это чудо.

Отправьте это своим коллегам-программистам, чтобы они тоже смогли узнать об этом.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *