如何从UTF-16字符串确定Unicode字符?

时间:2016-05-11 18:31:43

标签: c# string unicode character-encoding utf-16

我的字符串包含一个奇怪的Unicode空格字符,但我不确定它是什么字符。据我所知,在C#中,内存中的字符串使用UTF-16格式进行编码。确定哪些Unicode字符构成字符串的好方法是什么?

此问题被标记为可能重复 Determine a string's encoding in C# 它并不是这个问题的重复,因为我没有询问编码是什么。我已经知道C#中的字符串被编码为UTF-16。我只想要一种简单的方法来确定字符串中的Unicode值。

2 个答案:

答案 0 :(得分:1)

BMP字符的长度最多为2个字节(值0x0000-0xffff),因此那里的覆盖范围很大。中文,泰文,甚至是蒙古字母的字符都在那里,所以如果你不是编码专家,如果你的代码只处理BMP字符,你可能会被原谅。但同样地,像http://www.fileformat.info/info/unicode/char/10330/index.htm这样的字符不会被假定它适合两个字节的代码正确处理。

答案 1 :(得分:0)

Unicode似乎将字符标识为数字代码点。然而,并非所有代码点都实际引用字符,因为Unicode具有combining characters的概念(我不太了解)。但是,每个Unicode字符串,甚至一些无效的字符串(例如,非法的组合字符序列),都可以被视为代码点(数字)列表。

在UTF-16编码中,每个代码点被编码为2或4字节序列。在.net中,Char可能大致对应于2字节的UTF-16序列或4字节UTF-16序列的一半。当Char包含4字节序列的一半时,它被认为是“代理”,因为它只有在与必须保留的另一个Char组合时才有意义。要开始检查.net字符串,您可以让.net告诉您字符串中包含的代码点,必要时自动将代理对组合在一起。 .net提供Char.ConvertToUtf32,其描述方式如下:

  

将字符串中指定位置的UTF-16编码字符或代理项对的值转换为Unicode代码点。

documentation for Char.ConvertToUtf32(String s, Int32 index)表示在下列情况下会引发ArgumentException

  

指定的索引位置包含代理项对,并且该对中的第一个字符不是有效的高代理项,或者该对中的第二个字符不是有效的低代理项。

因此,您可以在字符串中逐个字符,并在Char.IsHighSurrogate()Char.ConvertToUtf32()的帮助下查找所有Unicode代码点。当您没有遇到高代理时,当前字符适合一个Char,您只需要在字符串中前进一个Char。如果你确实遇到过高代理人,那么这个角色需要两个Char,你需要提前两个:

static IEnumerable<int> GetCodePoints(string s)
{
    for (var i = 0; i < s.Length; i += char.IsHighSurrogate(s[i]) ? 2 : 1)
    {
        yield return char.ConvertToUtf32(s, i);
    }
}

当您说“来自UTF-16字符串”时,这可能意味着您已读取格式为UTF-16的一系列字节。如果是这种情况,您需要在传递给上述方法之前将其转换为.net字符串:

GetCodePoints(Encoding.UTF16.GetString(myUtf16Blob));

另一个注意事项:根据您构建String实例的方式,它可能包含一个关于代理对的非法Char序列。对于这样的字符串,Char.ConvertToUtf32()会在遇到时抛出异常。但是,我认为Encoding.GetString()将始终返回有效字符串或抛出异常。因此,通常,只要您的String实例来自“好”来源,您就不必担心Char.ConvertToUtf32()投掷(除非您传入索引偏移的随机值,因为您的偏移量可能在代理对中间。)