Ответ 1
В Unicode у вас есть кодовые точки. Это 21 бит длиной. Ваш персонаж 𝐀, Mathematical Bold Capital A
жирный Mathematical Bold Capital A
, имеет кодовую точку U + 1D400.
В кодировках Unicode у вас есть кодовые единицы. Это естественная единица кодирования: 8-битная для UTF-8, 16-битная для UTF-16 и так далее. Одна или несколько кодовых единиц кодируют одну кодовую точку.
В UTF-16 две кодовые единицы, которые образуют одну кодовую точку, называются суррогатной парой. Суррогатные пары используются для кодирования любой кодовой точки больше 16 бит, то есть U + 10000 и выше.
Это становится немного сложнее в .NET, так как .NET Char
представляет собой единицу кода UTF-16, а .NET String
представляет собой набор единиц кода.
Таким образом, ваша кодовая точка 𝐀 (U + 1D400) не может уместиться в 16 бит и нуждается в суррогатной паре, что означает, что ваша строка содержит две кодовые единицы:
var highUnicodeChar = "𝐀";
char a = highUnicodeChar[0]; // code unit 0xD835
char b = highUnicodeChar[1]; // code unit 0xDC00
То есть, когда вы индексируете строку таким образом, вы фактически получаете только половину суррогатной пары.
Вы можете использовать IsSurrogatePair для проверки суррогатной пары. Например:
string GetFullCodePointAtIndex(string s, int idx) =>
s.Substring(idx, char.IsSurrogatePair(s, idx) ? 2 : 1);
Важно отметить, что кроличья нора переменной кодировки в Unicode не заканчивается в кодовой точке. Кластер графем - это "видимая вещь", которую большинство людей, когда ее спрашивают, в конечном итоге называют "персонажем". Кластер графемы состоит из одной или нескольких кодовых точек: базовый символ и ноль или более комбинирующих символов. Примером комбинирующего символа является умлаут или различные другие украшения/модификаторы, которые вы можете добавить. Посмотрите этот ответ для ужасающего примера того, что могут сделать комбинирующие персонажи.
Чтобы проверить объединяющий символ, вы можете использовать GetUnicodeCategory, чтобы проверить наличие метки вложения, непространственного или межстрочного знака.