c# – 使用.Net的大于2个字节的unicode字符

前端之家收集整理的这篇文章主要介绍了c# – 使用.Net的大于2个字节的unicode字符前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我使用这个代码生成U 10FFFC
var s = Encoding.UTF8.GetString(new byte[] {0xF4,0x8F,0xBF,0xBC});

我知道这是私人使用的,但它显示一个单一的字符,我期望显示它.操作这个unicode字符时出现问题.

如果我以后这样做:

foreach(var ch in s)
{
    Console.WriteLine(ch);
}

而不是打印单个字符,它打印两个字符(即字符串显然由两个字符组成).如果我改变我的循环,将这些字符添加回一个空字符串,如下所示:

string tmp="";
foreach(var ch in s)
{
    Console.WriteLine(ch);
    tmp += ch;
}

最后,tmp将只打印一个字符.

这里究竟发生了什么?我以为char包含一个unicode字符,我从来不必担心一个字符有多少个字节,除非我正在转换为字节.我的真实用例是,我需要能够检测到在字符串中使用非常大的unicode字符.目前我有这样的事情:

foreach(var ch in s)
{
    if(ch>=0x100000 && ch<=0x10FFFF)
    {
        Console.WriteLine("special character!");
    }
}

然而,由于这种分割非常大的角色,这不行.我该如何修改这项工作?

解决方法

U 10FFFC是一个Unicode代码点,但字符串的接口不会直接公开一系列Unicode代码点.它的接口暴露了一系列UTF-16代码单元.这是一个非常低级的文本视图.非常不幸的是,这样一个低级的文本视图被嫁接到最明显和直观的界面可用…我将尽量不要太多关于我不喜欢这样的设计,只是说不要紧多么不幸,这只是一个(悲伤的)事实,你必须住.

首先,我会建议您使用char.ConvertFromUtf32获取您的初始字符串.更简单,更易读:

var s = char.ConvertFromUtf32(0x10FFFC);

所以,这个字符串的长度不是1,因为正如我所说,接口处理UTF-16代码单元,而不是Unicode代码点. U 10FFFC使用两个UTF-16代码单元,所以s.Length是2. U FFFF以上的所有代码点都需要两个UTF-16代码单元来表示.

您应该注意ConvertFromUtf32不返回char:char是UTF-16代码单元,而不是Unicode代码点.为了能够返回所有Unicode代码点,该方法不能返回单个字符.有时它需要返回两个,这就是为什么它使它成为一个字符串.有时你会发现一些API处理int而不是char,因为int可以用于处理所有的代码点(ConvertFromUtf32作为参数,ConvertToUtf32是什么样的结果).

字符串实现IEnumerable< char>,这意味着当您迭代字符串时,每次迭代可获得一个UTF-16代码单元.这就是为什么迭代你的字符串并打印出来,会产生一些破坏的输出,其中有两个“东西”.那些是组成U 10FFFC表示的两个UTF-16代码单元.他们被称为“代理人”.第一个是高/中等代价,第二个是低/低代价.当您单独打印它们时,它们不会产生有意义的输出,因为单个代理在UTF-16中甚至无效,并且它们也不被视为Unicode字符.

当您将这两个代理附加到循环中的字符串时,您可以有效地重建代理对,并在以后打印该对,从而获得正确的输出.

在咆哮的前面,请注意,没有人抱怨在该循环中使用了畸形的UTF-16序列.它创建一个具有孤立代理的字符串,但是一切都没有发生:字符串类型甚至不是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);
}

猜你在找的C#相关文章