Swift.String и Юникод

Если вас смущают отношения между строками Swift и их представлениями в Юникоде, эта статья для вас.

В C, C++, JavaScript можно легко получить целочисленный код заданной буквы и работать со строкой, как с массивом целых чисел. В некоторых алгоритмах может быть полезно представлять строку таким образом, но я не мог понять, как это сделать в Swift. Я ожидал чего-то вроде этого:

let helloString = "Hello"
let eCode = helloString.codePoint(at: 1) // expected String method
print("'e' =", eCode) // 'e' = 101

но в конце концов я закончил с этим:

Давайте разберемся с этим.

Юникод

Стандарт Unicode описывает более 137 000 символов и способы работы с ними. Проще говоря, Unicode определяет огромную таблицу различных символов:

каждый из которых имеет соответствующее целочисленное значение. Эти символы называются скалярами Unicode: между целочисленным кодом и символом или частью символа существует отношение один к одному.

Я бы не писал эту статью, если бы это были все определения стандарта Unicode. Существует также список правил обработки скаляров Unicode для получения символов, которых нет в таблице. Да, люди сгенерировали гораздо больше символов, чем может поместиться в таблице:

С таблицей и правилами объединения скаляров Unicode мы можем получить способ отразить любой символ в одно или несколько целочисленных значений. Для любого скалярного стандарта Unicode резервируется 21 бит для его кодирования. Наши компьютеры в основном работают с целыми числами длины: 8, 16, 32, … бит. Таким образом, стандарт Unicode также описывает, как кодировать строки Unicode целыми числами длиной 8 и 16 бит, определяя кодировки UTF-8 и UTF-16 соответственно.

Swift.String

Все проблемы в информатике можно решить на другом уровне косвенности, Дэвид Уилер

Строки Swift полностью поддерживают Unicode. Это означает, что строка «Флаг-🇺🇸» имеет длину (количество) 6 символов. Это особенно удобно, если вы когда-нибудь пытались получить количество символов в некоторых других языках — когда вы видите 6 символов и получаете длину 9!

Таким образом, тип Swift.String инкапсулирует всю сложность стандарта Unicode (не стесняйтесь открывать и читать). И это здорово, но как нам получить заветные целочисленные значения символов строки? Давайте рассмотрим отношения между различными типами вокруг типа String:

В листинге выше и на схеме в шапке статьи мы видим типы, разработанные инженерами Swift для соединения удобочитаемой String как массива символов и его целочисленных представлений при кодировании. Лучший способ познакомиться с ними поближе — прочитать официальную Документацию по Swift.String.

Вывод

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

  1. Строка представляет собой последовательность символов
  2. Каждый символ может быть закодирован одним или несколькими скалярами Unicode.
  3. Для каждого скаляра Unicode требуется максимум 21 бит, но существуют кодировки UTF-8 и UTF-16, разработанные для представления этих 21 бит в более коротких целочисленных типах.
  4. Swift.String и Swift.Character имеют свойство unicodeScalars для получения последовательности скаляров Unicode.
  5. Используя инициализатор массива с этим типом UnicodeScalarView, мы можем получить массив целых чисел UInt32, который кодирует данную строку.