U + 10FFFC - это одна кодовая точка Юникода, но интерфейс string
не отображает последовательность кодов Unicode напрямую. Его интерфейс предоставляет последовательность кодовых блоков UTF-16. Это очень низкоуровневое представление текста. Очень печально, что такое низкоуровневое представление текста было перенесено на наиболее очевидный и интуитивно понятный интерфейс ... Я постараюсь не рассказать о том, как мне не нравится этот дизайн, и просто скажу, что не важно как печально, это просто (грустный) факт, с которым вам нужно жить.
Прежде всего, я предлагаю использовать char.ConvertFromUtf32
, чтобы получить начальную строку. Намного проще, гораздо более удобным для чтения:
var s = char.ConvertFromUtf32(0x10FFFC);
Таким образом, эта строка-х Length
не 1, потому что, как я уже сказал, дело интерфейса в UTF-16 единиц кода, а не Unicode код точек. U + 10FFFC использует два кодовых блока UTF-16, поэтому s.Length
- 2. Все кодовые точки выше U + FFFF требуют для их представления двух кодовых блоков UTF-16.
Следует отметить, что ConvertFromUtf32
не возвращает char
: char
- это кодовый блок UTF-16, а не кодовая точка Юникода. Чтобы иметь возможность возвращать все кодовые точки Unicode, этот метод не может вернуть один char
. Иногда ему нужно вернуть два, и именно поэтому он делает его строкой. Иногда вы найдете некоторые API, относящиеся к int
s вместо char
, потому что int
может использоваться для обработки всех кодовых точек (это то, что ConvertFromUtf32
принимает в качестве аргумента и что дает результат ConvertToUtf32
).
string
инвентарь IEnumerable<char>
, что означает, что при переходе по string
вы получаете один кодовый блок UTF-16 на итерацию. Вот почему итерация вашей строки и ее распечатка дает некоторый сломанный вывод с двумя «вещами» в нем. Это два блока кода UTF-16, которые составляют представление U + 10FFFC.Их называют «суррогатами». Первый - суррогат высокого/свинцового, а второй - суррогат с низким/низким уровнем. Когда вы печатаете их отдельно, они не создают значимого вывода, потому что одиночные суррогаты даже не действуют в UTF-16, и они также не считаются символами Unicode.
Когда вы добавляете эти два суррогата в строку в цикле, вы фактически восстанавливаете суррогатную пару и печатаете эту пару позже , так как один получает правильный результат.
И в прощальном фронте обратите внимание, как ничто не жалуется на то, что вы использовали неправильную последовательность UTF-16 в этом цикле. Он создает строку с одиночным суррогатом, и все же все происходит так, как будто ничего не произошло: тип string
не является даже типом хорошо сформированным последовательностями кода кода UTF-16, но тип любой UTF-16 последовательность кода.
The char
structure предоставляет статические методы для борьбы с суррогатами: IsHighSurrogate
, IsLowSurrogate
, IsSurrogatePair
, ConvertToUtf32
и ConvertFromUtf32
. Если вы хотите, вы можете написать итератор, более символов Unicode, а не UTF-16 кодовых блоков:
static IEnumerable<int> AsCodePoints(this string s)
{
for(int i = 0; i < s.Length; ++i)
{
yield return char.ConvertToUtf32(s, i);
if(char.IsHighSurrogate(s, i))
i++;
}
}
Тогда можно перебирать, как:
foreach(int codePoint in s.AsCodePoints())
{
// do stuff. codePoint will be an int will value 0x10FFFC in your example
}
Если вы предпочитаете, чтобы получить каждую точку кода как строка вместо изменить тип возвращаемого на IEnumerable<string>
и выход линию:
yield return char.ConvertFromUtf32(char.ConvertToUtf32(s, i));
с этой версией, следующие работы как есть:
foreach(string codePoint in s.AsCodePoints())
{
Console.WriteLine(codePoint);
}
System.Globalization.StringInfo - это путь. Остальная часть кода неверна. Посмотрите: https://msdn.microsoft.com/en-us/library/system.globalization.stringinfo(v=vs.110).aspx – X181
Неясно, что вы имеете в виду. Есть ли проблема с кодом из этого ответа? –