将Unicode代理项对转换为文字字符串

har*_*gle 16 .net c# unicode unicode-escapes

我试图从一个字符串到另一个字符串读取高Unicode字符.为简洁起见,我将简化我的代码,如下所示:

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

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

当我直接分配highUnicodeCharresult1,它保留其文字值. When I try to access it by index, it returns \ud835.据我了解,这是一对代表UTF-32字符的替代UTF-16字符.我很确定这个问题与尝试隐式转换char为a有关string.

最后,我想得到与之result2相同的价值result1.我怎样才能做到这一点?

Cor*_*son 25

在Unicode中,您有代码点.这些是21位长.你的角色"数学大胆的资本A"的代码点为U + 1D400.

在Unicode编码中,您有代码单元.这些是编码的自然单位:UTF-8为8位,UTF-16为16位,依此类推.一个或多个代码单元编码单个代码点.

在UTF-16中,形成单个代码点的两个代码单元称为代理对.代理对用于编码大于16位的任何代码点,即U + 10000及以上.

这在.NET中有点棘手,因为.NET Mathematical Bold Capital A代表单个UTF-16代码单元,而.NET Char是代码单元的集合.

所以你的代码点(U + 1D400)不能容纳16位并且需要一个代理对,这意味着你的字符串中有两个代码单元:

var highUnicodeChar = "";
char a = highUnicodeChar[0]; // code unit 0xD835
char b = highUnicodeChar[1]; // code unit 0xDC00
Run Code Online (Sandbox Code Playgroud)

当你像这样索引字符串时的含义,你实际上只获得代理对的一半.

您可以使用IsSurrogatePair来测试代理对.例如:

string GetFullCodePointAtIndex(string s, int idx) =>
    s.Substring(idx, char.IsSurrogatePair(s, idx) ? 2 : 1);
Run Code Online (Sandbox Code Playgroud)

需要注意的是,Unicode中的变量编码兔洞不会在代码点结束.一字形集群是"看得见的东西"大多数人当被问及最终将所谓的"字符".字形集群由一个或多个代码点组成:基本字符和零个或多个组合字符.组合字符的一个示例是您可能想要添加的变音符号或各种其他装饰/修饰符.请参阅此答案,了解组合字符可以做什么的可怕示例.

要测试组合字符,可以使用GetUnicodeCategory检查封闭标记,非间距标记或间距标记.


dbc*_*dbc 8

看来你想从用户的角度(即第一个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();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以:

var result2 = highUnicodeChar.TextElements().FirstOrDefault() ?? "";
Run Code Online (Sandbox Code Playgroud)

请注意,StringInfo.GetTextElementEnumerator()还将组的Unicode 组合字符,所以该字符串的第一字形集群H?=T?+V?H?不会H.

这里示例小提琴.