Конвертировать суррогатную пару Unicode в буквальную строку - PullRequest
0 голосов
/ 01 октября 2018

Я пытаюсь прочитать высокий символ Unicode из одной строки в другую.Для краткости я упросту свой код, как показано ниже:

public static void UnicodeTest()
{
    var highUnicodeChar = "?"; //Not the standard A

    var result1 = highUnicodeChar; //this works
    var result2 = highUnicodeChar[0].ToString(); // returns \ud835
}

Когда я присваиваю highUnicodeChar непосредственно result1, он сохраняет свое буквальное значение ?.Когда я пытаюсь получить доступ к нему по индексу, он возвращает \ud835.Насколько я понимаю, это суррогатная пара символов UTF-16, используемая для представления символа UTF-32.Я почти уверен, что эта проблема связана с попыткой неявного преобразования char в string.

В конце я хочу, чтобы result2 выдал то же значение, что и result1.Как я могу это сделать?

Ответы [ 2 ]

0 голосов
/ 01 октября 2018

Похоже, что вы хотите извлечь первый «атомарный» символ с точки зрения пользователя (то есть первый Unicode кластер графем ) из строки highUnicodeChar, где «атомарный» символ включает в себяобе половины суррогатной пары .

Вы можете использовать StringInfo.GetTextElementEnumerator(), чтобы сделать это, разбив string на атомные порции, затем взяв первое.

Сначала определите следующий метод расширения:

public static class TextExtensions
{
    public static IEnumerable<string> TextElements(this string s)
    {
        // StringInfo.GetTextElementEnumerator is a .Net 1.1 class that doesn't implement IEnumerable<string>, so convert
        if (s == null)
            yield break;
        var enumerator = StringInfo.GetTextElementEnumerator(s);
        while (enumerator.MoveNext())
            yield return enumerator.GetTextElement();
    }
}

Теперь вы можете сделать:

var result2 = highUnicodeChar.TextElements().FirstOrDefault() ?? "";

Обратите внимание, что StringInfo.GetTextElementEnumerator() также сгруппирует Unicode , объединяющий символы , так что первый кластер графем изстрока Ĥ=T̂+V̂ будет , а не H.

Пример скрипта здесь .

0 голосов
/ 01 октября 2018

В Юникод , у вас есть кодовые точки .Это 21 бит длиной.Ваш персонаж ?, 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 , чтобы проверить наличие вмещающей метки,непространственный знак или промежуточный знак.

...