我有一个字符范围限制列表,我需要检查字符串,但char
.NET中的类型是UTF-16,因此一些字符变为古怪(代理)对.因此,当枚举a中的所有char
's时string
,我没有获得32位Unicode代码点,并且某些与高值的比较失败.
我非常了解Unicode,如果有必要,我可以自己解析字节,但我正在寻找一个C#/ .NET Framework BCL解决方案.所以......
如何将a转换为32位Unicode代码点string
的数组(int[]
)?
你问的是代码点.在UTF-16(C#char
)中,只有两种可能性:
该字符来自Basic Multilingual Plane,由单个代码单元编码.
该字符在BMP之外,并使用代理高低对代码单元进行编码
因此,假设字符串有效,则返回给定字符串的代码点数组:
public static int[] ToCodePoints(string str) { if (str == null) throw new ArgumentNullException("str"); var codePoints = new List(str.Length); for (int i = 0; i < str.Length; i++) { codePoints.Add(Char.ConvertToUtf32(str, i)); if (Char.IsHighSurrogate(str[i])) i += 1; } return codePoints.ToArray(); }
代理对 and a composed character
ñ
:
ToCodePoints("\U0001F300 El Ni\u006E\u0303o"); // El Niño // { 0x1f300, 0x20, 0x45, 0x6c, 0x20, 0x4e, 0x69, 0x6e, 0x303, 0x6f } // E l N i n ?? o
这是另一个例子.这两个代码点代表一个带有断音重音的第32个音符,两个代理对:
ToCodePoints("\U0001D162\U0001D181"); // // { 0x1d162, 0x1d181 } // ?
当C标准化时,它们被分解为一个符头,结合词干,组合旗帜和组合口音 - 断奏,所有代理对:
ToCodePoints("\U0001D162\U0001D181".Normalize()); // // { 0x1d158, 0x1d165, 0x1d170, 0x1d181 } // ?
请注意,leppie的解决方案不正确.问题是关于代码点,而不是文本元素.文本元素是代码点的组合,它们一起形成单个字素.例如,在上面的示例ñ
中,字符串中的字符串由拉丁文小写字母表示,n
后跟组合代字号??
.Leppie的解决方案会丢弃任何无法归一化为单个代码点的组合字符.
这个答案是不正确的.请参阅@ Virtlink的答案,了解正确答案.
static int[] ExtractScalars(string s) { if (!s.IsNormalized()) { s = s.Normalize(); } Listchars = new List ((s.Length * 3) / 2); var ee = StringInfo.GetTextElementEnumerator(s); while (ee.MoveNext()) { string e = ee.GetTextElement(); chars.Add(char.ConvertToUtf32(e, 0)); } return chars.ToArray(); }
注意:处理复合字符需要规范化.