先想想看,你认为下面代码返回值是多少?

"hello".IndexOf("", 2);
"hello".IndexOf("\0", 2);
"hello".IndexOf('\0', 2);

今天和大家分享关于.net core中与字符相关的一些奇怪问题。

首先我们先以.NET8目标框架做为测试环境。直接上代码:

using System.Reflection;
using System.Runtime.Versioning;
namespace TestNetCore
{
internal class Program
{
static void Main(string[] args)
{
var assembly = Assembly.GetExecutingAssembly();
var targetFramework = (TargetFrameworkAttribute?)Attribute.GetCustomAttribute(assembly, typeof(TargetFrameworkAttribute));
if (targetFramework != null)
{
Console.WriteLine($"目标框架: {targetFramework.FrameworkName}");
}
Console.WriteLine(@"""hello"".IndexOf("""", 2) 结果:" + "hello".IndexOf("", 2));
Console.WriteLine(@"""hello"".IndexOf(""\0"", 2) 结果:" + "hello".IndexOf("\0", 2));
Console.WriteLine(@"""hello"".IndexOf('\0', 2) 结果:" + "hello".IndexOf('\0', 2));
Console.ReadKey();
}
}
}

运行结果如下:

这与你设想的结果有差别吗?虽然只是一个方法,三行代码,但是这里面包含了很多知识点,下面我们就来具体聊聊为什么是这样的结果,底层逻辑是什么。

相信大多数人会有一下几个疑问:

①  为什么查找空字符串返回2

②  为什么查找“\0”也返回2

③  为什么查找‘\0‘又返回-1

1、为什么"hello".IndexOf("", 2)返回2

下面我们一个一个来说,首先来说为什么"hello".IndexOf("", 2)返回2,这个问题比较简单,就是方法本身定义问题,可以查看官方文档有详细说明,如下图:

方法定义如此,如果查询的值为空字符串,则返回值为startIndex,即查找起始位置索引,因为"hello".IndexOf("", 2)的2表示从索引2处开始查找,所以此这行代码返回2。

2、为什么"hello".IndexOf("\0", 2)也返回2

虽然这里只是简单的把空字符串改成了“\0”,但是里的问题就比较复杂了,涉及到多个知识点,首先这里涉及到Unicode编码问题,当前文化设置问题,以及.NET全球化问题。

我们先回到这个问题,先来看看官方文档说明,如下图:

从这里面可以大胆猜测“\0”就是属于可忽略字符,并且如果查询的字符串包含了可忽略字符,则结果和移除该字符搜索等效,那么"hello".IndexOf("\0", 2)就等效与"hello".IndexOf("", 2),因此返回2也就顺利成章了。

首先我们猜测是正确的,“\0的确属于可忽略字符,这就是Unicode编码规范问题,而且不单单“\0“会有这样的问题,Unicdoe可忽略字符都会有这样的问题,比如”\u0010“、”\u001B“等。

Console.WriteLine(@"""hello"".IndexOf(""\u0010"", 2) 结果:" + "hello".IndexOf("\u0010", 2));
Console.WriteLine(@"""hello"".IndexOf(""\u001B"", 2) 结果:" + "hello".IndexOf("\u001B", 2));

执行效果如下

3、为什么"hello".IndexOf(‘\0’, 2)返回-1

这个答案在上面的官方文档说明中也可以看到蛛丝马迹,“在执行语言性的或区分区域性的比较时该字符不被考虑“,这句话是关键,说明IndexOf方法是会受当前文化设置影响的,虽然我们写的代码里没有看到相关当前文化设置,但是不代表没有,我们可以看下IndexOf相关的重载方法。

红框中StringComparison参数就可以设置当前文化。我们看看有哪些设置选项。

因为"hello".IndexOf("\0", 2)内部使用了StringComparison.CurrentCulture  而"hello".IndexOf(‘\0’, 2) 内部使用了StringComparison.Ordinal,就是因为CurrentCulture枚举值导致“在执行语言性的或区分区域性的比较时”\0“不被考虑“,被直接忽略了,而Ordinal枚举值不会有这样的问题,所以没有被忽略,所以"hello".IndexOf(‘\0’, 2)返回-1。

我们也可以直接调用IndexOf重载方法,指定StringComparison来达到我们想要的效果。

Console.WriteLine(@"""hello"".IndexOf(""\0"", 2, 3, StringComparison.CurrentCulture) 结果:" + "hello".IndexOf("\0", 2, 3, StringComparison.CurrentCulture));
Console.WriteLine(@"""hello"".IndexOf(""\0"", 2, 3, StringComparison.Ordinal) 结果:" + "hello".IndexOf("\0", 2, 3, StringComparison.Ordinal));

运行代码如下:

虽然原因找到了,但是我们再深入思考一下,为什么会有这样的差异呢?只有IndexOf方法有这样的问题吗?

要回答这个问题,就是我们上面提到的.NET全球化问题了。在 .NET 5 前,.NET 全球化 API 在不同的平台上使用不同的基础库。 在 Unix 上,API 使用 Unicode 国际组件 (ICU),在 Windows 上,API 使用 区域语言支持 (NLS)。 这导致在不同平台上运行应用程序时,在少数全球化 API 中存在一些行为差异。 但是以下方面存在明显的行为差异:区域性和区域性数据、字符串大小写、字符串排序和搜索、排序关键字、字符串规范化、国际化域名 (IDN) 支持、Linux 上的时区显示名称。

因此不单单IndexOf方法有这样的问题,下面这些API都有存在同样的问题:

System.String.Compare
System.String.EndsWith
System.String.IndexOf
System.String.StartsWith
System.String.ToLower
System.String.ToLowerInvariant
System.String.ToUpper
System.String.ToUpperInvariant
System.Globalization.TextInfo(大多数成员)
System.Globalization.CompareInfo(大多数成员)
System.Array.Sort(对字符串数组进行排序时)
System.Collections.Generic.List<T>.Sort()(当列表元素为字符串时)
System.Collections.Generic.SortedDictionary<TKey,TValue>(当键为字符串时)
System.Collections.Generic.SortedList<TKey,TValue>(当键为字符串时)
System.Collections.Generic.SortedSet<T>(当集包含字符串时)

这里面很多方法都是有多个重载方法的,而每个重载方法默认当前文化设置可能并不相同。因此大家在开发的时候一定要注意使用,一不小心肯能就好引起一些奇怪的问题,因此大家尽量自己手动指定当前文化设置。

下表列出一些方法其对应的默认行为。

:当然如果调用方提供显式 CultureInfo 或 StringComparison 参数,则该参数将优先于任何默认值。

最后总结一下

1、 IndexOf对于Empty字符查找会返回开始查找索引startIndex,而不是我们想象中的-1;

2、 Unicode可忽略字符受StringComparison参数影响很大,会直接把相应字符直接忽略掉

3、 .NET全球化进程中,区域语言支持 (NLS)在向 Unicode 国际组件 (ICU)迁移是必然,因此我们在使用相关方法时一定要小心

4、 如果可以尽量主动显示设置当前文化区域设置

C#/.net core “hello”.IndexOf(“\0”,2)中的坑的更多相关文章

  1. ASP.NET CORE MVC 2.0 项目中引用第三方DLL报错的解决办法 - InvalidOperationException: Cannot find compilation library location for package

    目前在学习ASP.NET CORE MVC中,今天看到微软在ASP.NET CORE MVC 2.0中又恢复了允许开发人员引用第三方DLL程序集的功能,感到甚是高兴!于是我急忙写了个Demo想试试,我 ...

  2. Asp.net Core 1.0.1升级到Asp.net Core 1.1.0 Preview版本发布到Windows Server2008 R2 IIS中的各种坑

    Asp.net Core 1.0.1升级到Asp.net Core 1.1.0后,程序无法运行了 解决方案:在project.json中加入runtime节点 "runtimes" ...

  3. ASP.NET Core 3.0 WebApi中使用Swagger生成API文档简介

    参考地址,官网:https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/getting-started-with-swashbuckle?view ...

  4. NET Core 3.0 项目中使用 AutoFac

    .net core 3.1 今天已正式发布,3.1跟3.0差别不是很大,主要是对 3.0一小部分修复和完善,最重要的是.NET Core 3.1是长期支持(LTS)版本,建议大家升级. .net co ...

  5. ASP.NET Core 5.0 MVC中的 Razor 页面 介绍

    Razor 是一个用于将基于服务器的代码嵌入到网页中的标记语法. Razor语法由 Razor 标记.c # 和 HTML 组成. 通常包含 Razor 的文件的扩展名 cshtml Razor 语法 ...

  6. ASP.NET CORE MVC 2.0 如何在Filter中使用依赖注入来读取AppSettings,及.NET Core控制台项目中读取AppSettings

    问: ASP.NET CORE MVC 如何在Filter中使用依赖注入来读取AppSettings 答: Dependency injection is possible in filters as ...

  7. ASP.NET Core实现OAuth2.0的ResourceOwnerPassword和ClientCredentials模式

    前言 开发授权服务框架一般使用OAuth2.0授权框架,而开发Webapi的授权更应该使用OAuth2.0授权标准,OAuth2.0授权框架文档说明参考:https://tools.ietf.org/ ...

  8. [转]Could not load file or assembly 'System.Core, Version=2.0.5.0 和autofac冲突的问题

    Could not load file or assembly 'System.Core, Version=2.0.5.0 和autofac冲突的问题 来源:http://www.cnblogs.co ...

  9. Could not load file or assembly 'System.Core, Version=2.0.5.0 和autofac冲突的问题

    在部署到iis的时候会出现这个状况. 解决:下载安装这个补丁 http://support.microsoft.com/kb/2468871 http://www.microsoft.com/zh-c ...

  10. .net core 1.1.0 MVC 控制器接收Json字串 (JObject对象) (二)

    .net core 1.1.0 MVC 控制器接收Json字串 (JObject对象) (二) .net core 1.1.0 MVC 控制器接收Json字串 (JObject对象) (一) 上一篇主 ...

随机推荐

  1. 透视开源生态,OSGraph——GitHub全域数据图谱的智能洞察工具

    "透视开源生态,OSGraph--GitHub全域数据图谱的智能洞察工具 OSGraph (Open Source Graph) 是一个开源图谱关系洞察工具,基于GitHub开源数据全域图谱 ...

  2. [oeasy]python0010_怎么用命令行保存文件

    编写 py 文件 回忆上次内容 上次 真的输出了 程序员的浪漫 Hello world!   print函数 可以输出 字符串 但是 print这个词 别拼错 就连 大小写 也别能错 错了就改 也没事 ...

  3. [oeasy]python0074[专业选修]字节序_byte_order_struct_pack_大端序_小端序

    进制转化 回忆上次内容 上次 总结了 计算字符串值的函数 eval   四种进制的转化函数 bin oct int hex     函数名 前缀 目标字符串所用进制 bin 0b 二进制 oct 0o ...

  4. [oeasy]python0072_自定义小动物变色_cowsay_color_boxes_asciiart

    修改颜色 回忆上次内容 上次搞的是 颜色 前景颜色 总共有 7 种基本色 还有什么 好玩的 么? 可以 给小动物 上色 吗? 配合 先将cowsay结果 输出重定向 sudo apt install ...

  5. nacos启动失败:No DataSource set

    通过docker查看nacos的日志发现nacos好端端的突然不能用了 docker logs nacos 报错后说是no datasource set,我看了我在docker里的MySQL是正常启动 ...

  6. 【Vue】 签名组件

    一.需求背景: 检查业务,检查完成后,执行人需要签字证明检查完成 二.实现效果: 三.技术实现 通过canvas转换成blob对象,可以上传到文件服务,或者是下载另存为到本地磁盘 注意重点,canva ...

  7. 举例说明:ChatGPT和百度文心一言的差距

    翻译: we employ ten sub-generators against one discriminator 百度的表现: ChatGPT的表现:

  8. 光刻机巨头ASML公布了其最新的品牌短片《站在巨人的肩膀上》

    光刻机巨头ASML公布了其最新的品牌短片<站在巨人的肩膀上>: 荷兰光刻机:ASML使用AI工具midjourney和runway制作宣传片 这个时长1分50秒短片的特别地方在于,它是完全 ...

  9. baselines算法库common/vec_env/vec_env.py模块分析

    common/vec_env/vec_env.py模块内容: import contextlib import os from abc import ABC, abstractmethod from ...

  10. JavaWeb中的Tomcat,Servlet详解

    JavaWeb JavaWeb技术主要包括服务器技术(后端),如Tomcat,Servlet,JSP等待,以及客户端技术(前端)如HTML,CSS,JavaScript等等 Web服务器 Web服务器 ...