Как преобразовать суррогатную пару в Unicode скаляр в Swift

Следующий пример взят из Документация строк и символов:

enter image description here

Значения 55357 (U+D83D в шестнадцатеричном формате) и 56374 (U+DC36 в шестнадцатеричном виде) являются суррогатными парами, которые формируют скаляр Unicode U+1F436, который является символом DOG FACE. Есть ли способ пойти в другую сторону? То есть, могу ли я преобразовать суррогатную пару в скаляр?

Я пробовал

let myChar: Character = "\u{D83D}\u{DC36}"

но я получил ошибку "Неверный Unicode скалярный".

Этот ответ Objective C и этот проект, по-видимому, являются обычными решениями, но есть ли что-нибудь встроенное в Swift (особенно Swift 2.0+), которое это делает?

Ответ 1

Существуют формулы для вычисления исходной кодовой точки на основе суррогатной пары и наоборот. Из https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae:

Раздел 3.7 Стандарта Unicode 3.0 определяет алгоритмы преобразования в суррогатные пары и из них.

Кодовая точка C больше, чем 0xFFFF соответствует суррогатной паре <H, L> согласно следующей формуле:

H = Math.floor((C - 0x10000) / 0x400) + 0xD800
L = (C - 0x10000) % 0x400 + 0xDC00

Обратное отображение, т.е. от суррогатной пары <H, L> до Unicode кодовая точка C, задается:

C = (H - 0xD800) * 0x400 + L - 0xDC00 + 0x10000

Ответ 2

Учитывая последовательность кодовых единиц UTF-16 (например, 16-разрядные номера, например, вы получаете от String.utf16 или просто массив чисел), вы можете использовать тип UTF16 и его метод decode для превратите его в UnicodeScalars, который затем можно преобразовать в String.

Это немного шероховатый элемент, который принимает генератор (как он обрабатывает состояние) и возвращает перечисление, которое указывает результат (со связанным типом скаляра) или ошибку или завершение. Сравнение шаблонов Swift 2.0 делает его намного проще в использовании:

let u16data: [UInt16] = [0xD83D,0xDC36]
//or let u16data = "Hello, 🌍".utf16

var g = u16data.generate()
var s: String = ""
var utf16 = UTF16()
while case let .Result(scalar) = utf16.decode(&g) {
    print(scalar, &s)
}
print(s) // prints 🐶