Почему Свифт считает этот кластер графем двумя персонажами вместо одного?
Вообще, Свифт действительно умел считать графемные кластеры одним персонажем. Например, если я хочу сделать ливанский флаг, я могу объединить два символа Юникода
- U+1F1F1 РЕГИОНАЛЬНЫЙ ИНДИКАТОР СИМВОЛ ПИСЬМО L
- U + 1F1E7 РЕГИОНАЛЬНЫЙ ИНДИКАТОР СИМВОЛ ПИСЬМО В
и, как и ожидалось, это один из символов в Swift:
let s = "\u{1f1f1}\u{1f1e7}"
assert(s.characters.count == 1)
assert(s.utf16.count == 4)
assert(s.utf8.count == 8)
Однако, скажем, я хочу сделать смайлик для велосипедиста из Fitzpatrick Type-5. Если я объединю
- U+1F6B4 БИЦИКЛИСТ
- U+1F3FE EMOJI МОДИФИКАТОР FITZPATRICK TYPE-5
Свифт считает эту комбинацию двумя персонажами!
let s = "\u{1f6b4}\u{1f3fe}"
assert(s.characters.count == 2) // <----- WHY?
assert(s.utf16.count == 4)
assert(s.utf8.count == 8)
Почему это два символа вместо одного?
Чтобы показать, почему я ожидал, что это будет 1, обратите внимание, что этот кластер фактически интерпретируется как действительный эмодзи:
1 ответ
Часть ответа дана в сообщении об ошибке, упомянутом в комментарии emrys57. При разбиении строки Unicode на "символы" Swift, по-видимому, использует границы кластера Grapheme, определенные в текстовой сегментации Unicode UAX #29. Существует правило не разбивать символы региональных индикаторов, но для модификаторов Emoji такого правила не существует. Итак, согласно UAX #29, строка "\u{1f6b4}\u{1f3fe}"
содержит два графемных кластера. Посмотрите это сообщение от Кена Уистлера в списке рассылки Unicode для объяснения:
Это объясняется тем фактом, что резервное поведение для модификаторов просто в виде независимых пиктографических бликов, то есть изображений образцов цвета. [...] Вам нужны дополнительные конкретные знания об этих последовательностях - они не просто выпадают из реализации по умолчанию правил UAX #29 для кластеров графем.