.NET如何将字符串分隔为字符

前言

如果这是一道面试题,答案也许非常简单:.ToCharArray(),这基本正确……

我们以“AB吉??????”作为输入参数,首先如果按照“正常”处理的思路,用 .ToCharArray(),然后转换为 JSON(以便方便查看)返回结果如下:

[	"A",	"B",	"吉",	"�",	"�",	"�",	"�",	"�",	"�",	"‍",	"�",	"�",	"‍",	"�",	"�",	"‍",	"�",	"�"	
]

不出所料,出现了大量乱码。

正常一个字符( Unicode基平面)应该是占用一个 char(2字节)没错,但如果涉及 4字节 UnicodeEmoji,这个问题就不简单了。

  • 首先, 32位 Unicode占用两个 char,如:?;

  • 其次,某些 emoji可能占用超过两个 char,可能多达 11个,如:?‍?‍?‍?;

代码演示如图:

640?wx_fmt=png

下面我将一一演示我的解决过程。

32位Unicode 

我知道在 .NET中,如果一个 char无法容纳一个字符, char.IsHighSurrogate()方法传入这个 char就会返回 true,这时即可做处理。按照这个思路,解决方法如下:

IEnumerable<string> SplitToCharacters(string input)	
{	for (var i = 0; i < input.Length; ++i)	{	if (char.IsHighSurrogate(input[i]))	{	yield return input.Substring(i, 2);	++i;	}	else	{	yield return input[i].ToString();	}	}	
}

我将“AB吉??????”作为输入参数,运行结果如下:

[	"A",	"B",	"吉",	"?",	"?",	"?",	"‍",	"?",	"‍",	"?",	"‍",	"?"	
]

可见,它成功“破解”了 32Unicode,“?”显示正常,部分表情如?,也显示正常。但?‍?‍?‍?还是被“暴力”拆成了 4个表情“????”和三个空白。我稍后聊这个 Emoji,因为这些代码有简化空间。

后来我将这个“字符串分隔为字符”问题在长沙.NET技术社区发问,有大佬就指出有简单的办法,通过系统内置的 StringInfo类,即可一步到位解决:

IEnumerable<string> SplitToCharacters(string input)	
{	var si = new StringInfo(input);	for (var i = 0; i < si.LengthInTextElements; ++i)	{	yield return si.SubstringByTextElements(i, 1);	}	
}

返回值完全一样,更有大佬祭出了“骚操作”,通过 UTF32来解决,实在是暗暗佩服:

string[] SplitToCharacters(string input)	
{	byte[] bytes = Encoding.UTF32.GetBytes(input);	Span<int> span = MemoryMarshal.Cast<byte, int>(bytes);	var strings = new string[span.Length];	for (var i = 0; i < span.Length; ++i)	{	strings[i] = char.ConvertFromUtf32(span[i]);	}	return strings;	
}

返回值也完全一样。

然而这些办法都解决不了 Emoji的问题,那么 Emoji到底要如何才能解决呢?

Emoji

在一次偶然的机会,看 UWPWin2DGallery时,我看到了这个 demo

640?wx_fmt=png

我心想, DirectWrite既然知道每个字符的边界,显然也必然知道如何将字符串分隔为字符。果然,经过一阵探索,我找到了解决办法:

// 安装NuGet包:SharpDX.Direct2D1	
using SharpDX.DirectWrite;	
IEnumerable<string> SplitToCharacters(string text)	
{	using var dwrite = new Factory();	using var format = new TextFormat(dwrite, "Arial", 14.0f); // 字体字号无所谓	using var layout = new TextLayout(dwrite, text, format, int.MaxValue, int.MaxValue);	var pos = 0;	foreach (ClusterMetrics cm in layout.GetClusterMetrics())	{	yield return text.Substring(pos, cm.Length);	pos += cm.Length;	}	
}

运行效果如下:

[	"A",	"B",	"吉",	"?",	"?",	"?‍?‍?‍?"	
]

终于……完全正常!但这是基于 WindowsOnlyDirectWrite技术,有没有平台无关的方法呢?

经常我4个多小时的翻阅文档、编写代码,终于找到了眉目。文档如下:https://en.wikipedia.org/wiki/Zero-width_joiner

原来有一个“零宽度连接符”( Zero-width joiner/ ZWJ)的概念,值为 0x200D。如果发现 char为该值,则说明它是一个零宽度连接符,此时后面的 emoji应该与前面的 emoji连接。可以使用如下代码分析“?‍?‍?‍?”这个 emoji

IEnumerable<string> SplitToCharacters(string input)	
{	for (var i = 0; i < input.Length; ++i)	{	if (char.IsHighSurrogate(input[i]))	{	yield return input.Substring(i, 2);	++i;	}	else	{	yield return input[i].ToString();	}	}	
}	
SplitToCharacters("?‍?‍?‍?").Select(x => new	
{	Text = x, 	Code = String.Join("", x.Select(x => ((short)x).ToString("X4"))), 	
}).Dump();

运行结果如下——果然它包含了三个零宽度连接符:

640?wx_fmt=png

因此我们可以利用这个 0x200D,然后加几个 if/else,即可将问题解决:

IEnumerable<string> SplitToCharacters(string input)	
{	for (var i = 0; i < input.Length; ++i)	{	if (char.IsHighSurrogate(input[i]))	{	int length = 0;	while (true)	{	length += 2;	if (i + length < input.Length && input[i + length] == 0x200D)	{	length += 1;	}	else	{	break;	}	}	yield return input.Substring(i, length);	i += length - 1;	}	else	{	yield return input[i].ToString();	}	}	
}

效果与 DirectWrite完全一样,完美!

结语

说来话长,这其实是客户真正遇到的问题。事情起源于一次客户与我的微信聊天,客户遇到了一个问题:

640?wx_fmt=png

客户是想从简体中文转换为繁体中文,正使用 Microsoft.VisualBasic.dll提供的 Strings.StrConv(text,VbStrConv.TraditionalChinese)方法,遇到了这个问题。客户的代码如下:

Strings.StrConv("飞龙骑脸怎么输!?", VbStrConv.TraditionalChinese)

.NETFramework下输出结果是:飛龍騎臉怎么輸!??。注意,最后的 emoji表情"?"被显示成了两个问号“??”。

.NETCore下,该代码运行报异常,提示需要操作系统支持(可能需要安装语言包),具体报错内容是:“ ArgumentException:Thissystem doesnotcontain supportfortheTraditionalChineselocale.”。

这个区别说明,该函数最好别在 .NETCore上使用。

后来我找到了一个好办法,安装 NuGetCHTCHSConv,然后使用类似代码即可,结果为 飛龍騎臉怎么輸!?,完全正确。

ChineseConverter.Convert("飞龙骑脸怎么输!?", ChineseConversionDirection.SimplifiedToTraditional)

但我在寻求这个问题的过程中误入了另一条路,我想将字符串分隔开来,然后单独判断是不是一个 char能包含整个字符。虽然我后来知道解决这个问题不需要,也不应该这样做。但我在这条错误的路上越陷越深,然后出现了本篇文章?。

微信可能无法评论,请点击左下角“阅读原文”前往我的博客园点赞/留言。

喜欢的朋友 请关注我的微信公众号:【DotNet骚操作】

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313210.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Orleans 知多少 | 3. Hello Orleans

1. 引言是的&#xff0c;Orleans v3.0.0 已经发布了&#xff0c;并已经完全支持 .NET Core 3.0。所以&#xff0c;Orleans 系列是时候继续了&#xff0c;抱歉&#xff0c;让大家久等了。万丈高楼平地起&#xff0c;这一节我们就先来了解下Orleans的基本使用。2. 模板项目讲解在…

.NET Core 3.0之深入源码理解ObjectPool(二)

写在前面前文主要介绍了ObjectPool的一些理论基础&#xff0c;本文主要从源码角度理解Microsoft.Extensions.ObjectPool是如何实现的。下图为其三大核心组件图&#xff1a;核心组件ObjectPoolObjectPool是一个泛型抽象类&#xff0c;里面只有两个抽象方法&#xff0c;Get和Retu…

VC维学习

http://www.flickering.cn/machine_learning/2015/04/vc维的来龙去脉/ 说说历史Hoeffding不等式Connection to Learning学习可行的两个核心条件Effective Number of HypothesesGrowth FunctionBreak Point与ShatterVC BoundVC dimension深度学习与VC维小结参考文献 VC维在机器学…

.NET Core 3.0 一个 jwt 的轻量角色/用户、单个API控制的授权认证库

作者&#xff1a;痴者工良&#xff08;朋友合作原创&#xff09;来源&#xff1a;https://www.cnblogs.com/whuanle/p/11743406.html目录说明一、定义角色、API、用户二、添加自定义事件三、注入授权服务和中间件三、如何设置API的授权四、添加登录颁发 Token五、部分说明六、验…

.NET Core 3.0 构建和部署

Default Executables 默认可执行文件 在 dotnet build 或 dotnet publish 期间&#xff0c;将创建一个与你使用的 SDK 的环境和平台相匹配的可执行文件。 和其他本机可执行文件一样&#xff0c;可以使用这些可执行文件执行相同操作&#xff0c;例如&#xff1a; 可以双击可执行…

为什么我会了SOA,你们还要逼我学微服务?

菜菜哥&#xff0c;我最近需要做一个项目&#xff0c;老大让我用微服务的方式来做那挺好呀&#xff0c;微服务现在的确很流行我以前在别的公司都是以SOA的方式&#xff0c;SOA也是面向服务的方式呀的确&#xff0c;微服务和SOA有相同之处面向服务的架构&#xff08;SOA&#xf…

面对万物互联的智能世界,你是否也想分一杯羹

第六届世界互联网大会于10月20日至22日在浙江乌镇顺利举行。作为世界互联网大会“13”架构的重要组成部分&#xff0c;“互联网之光”博览会以“智能互联网、开放合作——携手共建网络空间命运共同体”为主题&#xff0c;集中展示了全球范围内的互联网新技术、新成果、新产品、…

你必须知道的容器监控 (2) cAdvisor

# 实验环境&#xff1a;阿里云ECS主机&#xff08;两台&#xff09;&#xff0c;CentOS 7.401—cAdvisor简介为了解决容器的监控问题&#xff0c;Google开发了一款容器监控工具cAdvisor&#xff08;Container Advisor&#xff09;&#xff0c;它为容器用户提供了对其运行容器的…

代码阅读

http://alanse7en.github.io/caffedai-ma-jie-xi-4/ 三. 从一个比较宏观的层面上去了解caffe怎么去完成一些初始化的工作和使用Solver的接口函数&#xff0c;本文将主要分为四部分的内容&#xff1a; Google Flags的使用Register Brew Function的宏的定义和使用train()函数的…

动手造轮子:实现一个简单的依赖注入(一)

动手造轮子&#xff1a;实现一个简单的依赖注入(一)Intro在上一篇文章中主要介绍了一下要做的依赖注入的整体设计和大概编程体验&#xff0c;这篇文章要开始写代码了&#xff0c;开始实现自己的依赖注入框架。类图首先来温习一下上次提到的类图服务生命周期服务生命周期定义&am…

Qt 调试Caffe

http://blog.csdn.net/xg123321123/article/details/52817658 1.下载并安装Qt Creator 下载页面&#xff0c;推荐使用4.x版本&#xff0c;比如&#xff1a; Qt Creator 4.1.0 for Linux 64-bit下载的是run包&#xff0c;安装方法&#xff1a; cd到下载目录 chmod x xxx.run …

.NET Core 3.0 本地工具

.NET Core从最早期的版本就开始支持全局工具了。如果仅仅需要在某个项目中或某个文件夹中使用特定的工具&#xff0c;那么.NET Core 3.0就允许您这样做。 使用.NET Core 3.0&#xff0c;您可以在特定的文件夹下安装“本地”工具&#xff0c;它的作用范围仅限于该文件夹及其子文…

Nsight 调试 Caffe

http://blog.csdn.net/u014114990/article/details/47723877 右键属性菜单的General->Code Analysis->Paths and Symbols下进行加入&#xff1a; Includes下加入程序需要用到的头文件的路径&#xff1a; Library Path下添加需要用到库文件的路径&#xff1a; 具体用到的…

张高兴的 .NET Core IoT 入门指南:(五)PWM 信号输出

什么是 PWM在解释 PWM 之前首先来了解一下电路中信号的概念&#xff0c;其中包括模拟信号和数字信号。模拟信号是一种连续的信号&#xff0c;与连续函数类似&#xff0c;在图形上表现为一条不间断的连续曲线。数字信号为只能取有限个数值的信号&#xff0c;比如计算机中的高电平…

.NET Core 3 对 IoT 应用程序的高级支持:System.Device.Gpio

System.Device.Gpio 是一个全新的 .Net Core 开源库&#xff0c;它旨在使 IoT&#xff08;物联网&#xff09;应用程序能够通过其 GPIO 引脚或其他 I/O 控制硬件与传感器、显示器和输入设备进行交互。该库是由社区维护的多个设备绑定集合来进行增强实现的。正如微软 .NET 项目…

代码阅读2

http://blog.csdn.net/u014568921/article/details/53995455 首先&#xff0c;要知道caffe里的卷积核都是三维的 在caffe中卷积核是三维的还是二维的&#xff1f; 下面分割线之间的内容来自http://blog.csdn.NET/u014114990/article/details/51125776 /*********************…

一文带你了解华为云DevCloud为何能全面领跑中国DevOps云服务市场

近日&#xff0c;国际权威调研机构IDC发布了《IDC MarketScape&#xff1a;中国DevOps云服务市场2019厂商评估》报告&#xff0c;该报告从战略和能力两个维度对国内主流DevOps云厂商进行了评估&#xff0c;报告显示&#xff0c;华为云位于 IDC MarketScape “中国DevOps云服务 …

[电子书制作]Excel催化剂输出内容汇总PDF及Word版本分享

Excel催化剂在2018年开始&#xff0c;陆续写出了230篇高质量原创性文章&#xff0c;将Excel催化剂插件的开发过程及使用方法全方位地通过文字的方式给广大网友们分享了。电子书下载方式同样地&#xff0c;为了减少大家过多繁琐的资料下载途径&#xff0c;电子书的下载路径和之前…

深入理解.NET Core的基元: deps.json, runtimeconfig.json, dll文件

原文链接&#xff1a; Deep-dive into .NET Core primitives: deps.json, runtimeconfig.json, and dlls作者&#xff1a; Nate McMasterC#编译器(The C# Compiler)C#的编译器可以将cs文件转换为dll文件, 即程序集文件。程序集文件是一个便携的可执行格式文件, 借助.NET Core,它…