之前在处理文本的时候遇到过这样的一个问题,有的字,它看着是一个,但是程序说他是两个,但是实际真的是一个。 好吧,你们一定没有听懂我在讲什么。没关系一起来看看代码


假如有这么一个字符串“黄腾霄好瘦哦”,需要使用程序找出这个字符串包含几个字,怎么找?

var s=“黄腾霄好瘦哦”;
var len=s.Length;

很简单是不是。那再看看这个字符串,“黄腾霄也能算瘦🤔”,不出意料的话应该是8个字对吧?

我们看看上一个程序的输出。

image-20191110153748683

惊了,他居然输出了长度为9。

实际上这个地方的问题出在最后一个emoji上。

让我们先看看这个字符串的Unicode编码是什么样的。

image-20191110154015899

我们看到这个8个字符的字符串实际上包含了9个Unicode。

image-20191110154034396

而其中整个emoji字符🤔,实际是由2个Unicode字符拼接而成的。

实际上对于“U+D800-U+DFFF”中的值是作为代理字符对存在的,他们会将两个字符映射成为一个字符。

所以在Unicode编码上是2个,而显示上却只有一个。而我们的String.Length恰好就只是读Unicode编码的个数。

所以才会出现多一个的情况。

那么怎么办呢?我们看看下面这个代码

var s = @"黄腾霄也能算瘦🤔";
var len = s.Length;
var info = new StringInfo(s);
var realLength = info.LengthInTextElements;

image-20191110154831398

成功!!StingInfo可以获取字符串实际显示的字符个数。

当然这样还不够,我们还是会想要枚举字符串中每个字符。

不过SringInfo并不是集合,也没有继承IEnumerable的接口

但是我们可以通过静态方法StringInfo.GetNextTextElement获得指定位置的显示字符

或者通过静态方法StringInfo.GetTextElementEnumerator获得指定字符串的显示字符迭代器。

我们看看如下代码

var s = @"黄腾霄也能算瘦🤔";
var len = s.Length;
var info = new StringInfo(s);
var realLength = info.LengthInTextElements;
Console.WriteLine(s);
for (int i = 0; i < realLength; i++)
{
    Console.WriteLine(StringInfo.GetNextTextElement(s, i));
}

Console.WriteLine("-----------");
var enumerator = StringInfo.GetTextElementEnumerator(s);
while (enumerator.MoveNext())
{
    Console.WriteLine(enumerator.GetTextElement());
}

image-20191110155645128

我们看到每个显示字符都成功迭代了。(PS:console下字体无法识别emoji字符,所以会使用??来显示,但是枚举方法是对的)


参考文献:

StringInfo Class (System.Globalization) - Microsoft Docs

UTF-16 - 维基百科,自由的百科全书


本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/%E4%BD%BF%E7%94%A8StringInfo%E6%AD%A3%E7%A1%AE%E6%9F%A5%E6%89%BE%E5%AD%97%E7%AC%A6%E4%B8%AA%E6%95%B0.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系