C#.net core 基礎(chǔ) - “hello”.IndexOf(“\0”,2)中的坑
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
先想想看,你認(rèn)為下面代碼返回值是多少? "hello".IndexOf("", 2); "hello".IndexOf("\0", 2); "hello".IndexOf('\0', 2); 今天和大家分享關(guān)于.net core中與字符相關(guān)的一些奇怪問(wèn)題。 首先我們先以.NET8目標(biāo)框架做為測(cè)試環(huán)境。直接上代碼: 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($"目標(biāo)框架: {targetFramework.FrameworkName}"); } Console.WriteLine(@"""hello"".IndexOf("""", 2) 結(jié)果:" + "hello".IndexOf("", 2)); Console.WriteLine(@"""hello"".IndexOf(""\0"", 2) 結(jié)果:" + "hello".IndexOf("\0", 2)); Console.WriteLine(@"""hello"".IndexOf('\0', 2) 結(jié)果:" + "hello".IndexOf('\0', 2)); Console.ReadKey(); } } } 運(yùn)行結(jié)果如下: 這與你設(shè)想的結(jié)果有差別嗎?雖然只是一個(gè)方法,三行代碼,但是這里面包含了很多知識(shí)點(diǎn),下面我們就來(lái)具體聊聊為什么是這樣的結(jié)果,底層邏輯是什么。 相信大多數(shù)人會(huì)有一下幾個(gè)疑問(wèn): ① 為什么查找空字符串返回2 ② 為什么查找“\0”也返回2 ③ 為什么查找‘\0‘又返回-1 1、為什么"hello".IndexOf("", 2)返回2下面我們一個(gè)一個(gè)來(lái)說(shuō),首先來(lái)說(shuō)為什么"hello".IndexOf("", 2)返回2,這個(gè)問(wèn)題比較簡(jiǎn)單,就是方法本身定義問(wèn)題,可以查看官方文檔有詳細(xì)說(shuō)明,如下圖: 方法定義如此,如果查詢的值為空字符串,則返回值為startIndex,即查找起始位置索引,因?yàn)?hello".IndexOf("", 2)的2表示從索引2處開(kāi)始查找,所以此這行代碼返回2。 2、為什么"hello".IndexOf("\0", 2)也返回2雖然這里只是簡(jiǎn)單的把空字符串改成了“\0”,但是里的問(wèn)題就比較復(fù)雜了,涉及到多個(gè)知識(shí)點(diǎn),首先這里涉及到Unicode編碼問(wèn)題,當(dāng)前文化設(shè)置問(wèn)題,以及.NET全球化問(wèn)題。 我們先回到這個(gè)問(wèn)題,先來(lái)看看官方文檔說(shuō)明,如下圖: 從這里面可以大膽猜測(cè)“\0”就是屬于可忽略字符,并且如果查詢的字符串包含了可忽略字符,則結(jié)果和移除該字符搜索等效,那么"hello".IndexOf("\0", 2)就等效與"hello".IndexOf("", 2),因此返回2也就順利成章了。 首先我們猜測(cè)是正確的,“\0的確屬于可忽略字符,這就是Unicode編碼規(guī)范問(wèn)題,而且不單單“\0“會(huì)有這樣的問(wèn)題,Unicdoe可忽略字符都會(huì)有這樣的問(wèn)題,比如”\u0010“、”\u001B“等。 Console.WriteLine(@"""hello"".IndexOf(""\u0010"", 2) 結(jié)果:" + "hello".IndexOf("\u0010", 2)); Console.WriteLine(@"""hello"".IndexOf(""\u001B"", 2) 結(jié)果:" + "hello".IndexOf("\u001B", 2)); 執(zhí)行效果如下 3、為什么"hello".IndexOf(‘\0’, 2)返回-1這個(gè)答案在上面的官方文檔說(shuō)明中也可以看到蛛絲馬跡,“在執(zhí)行語(yǔ)言性的或區(qū)分區(qū)域性的比較時(shí)該字符不被考慮“,這句話是關(guān)鍵,說(shuō)明IndexOf方法是會(huì)受當(dāng)前文化設(shè)置影響的,雖然我們寫的代碼里沒(méi)有看到相關(guān)當(dāng)前文化設(shè)置,但是不代表沒(méi)有,我們可以看下IndexOf相關(guān)的重載方法。 紅框中StringComparison參數(shù)就可以設(shè)置當(dāng)前文化。我們看看有哪些設(shè)置選項(xiàng)。 因?yàn)?hello".IndexOf("\0", 2)內(nèi)部使用了StringComparison.CurrentCulture 而"hello".IndexOf(‘\0’, 2) 內(nèi)部使用了StringComparison.Ordinal,就是因?yàn)镃urrentCulture枚舉值導(dǎo)致“在執(zhí)行語(yǔ)言性的或區(qū)分區(qū)域性的比較時(shí)”\0“不被考慮“,被直接忽略了,而Ordinal枚舉值不會(huì)有這樣的問(wèn)題,所以沒(méi)有被忽略,所以"hello".IndexOf(‘\0’, 2)返回-1。 我們也可以直接調(diào)用IndexOf重載方法,指定StringComparison來(lái)達(dá)到我們想要的效果。 Console.WriteLine(@"""hello"".IndexOf(""\0"", 2, 3, StringComparison.CurrentCulture) 結(jié)果:" + "hello".IndexOf("\0", 2, 3, StringComparison.CurrentCulture)); Console.WriteLine(@"""hello"".IndexOf(""\0"", 2, 3, StringComparison.Ordinal) 結(jié)果:" + "hello".IndexOf("\0", 2, 3, StringComparison.Ordinal)); 運(yùn)行代碼如下: 雖然原因找到了,但是我們?cè)偕钊胨伎家幌?,為什么?huì)有這樣的差異呢?只有IndexOf方法有這樣的問(wèn)題嗎? 要回答這個(gè)問(wèn)題,就是我們上面提到的.NET全球化問(wèn)題了。在 .NET 5 前,.NET 全球化 API 在不同的平臺(tái)上使用不同的基礎(chǔ)庫(kù)。 在 Unix 上,API 使用 Unicode 國(guó)際組件 (ICU),在 Windows 上,API 使用 區(qū)域語(yǔ)言支持 (NLS)。 這導(dǎo)致在不同平臺(tái)上運(yùn)行應(yīng)用程序時(shí),在少數(shù)全球化 API 中存在一些行為差異。 但是以下方面存在明顯的行為差異:區(qū)域性和區(qū)域性數(shù)據(jù)、字符串大小寫、字符串排序和搜索、排序關(guān)鍵字、字符串規(guī)范化、國(guó)際化域名 (IDN) 支持、Linux 上的時(shí)區(qū)顯示名稱。 因此不單單IndexOf方法有這樣的問(wèn)題,下面這些API都有存在同樣的問(wèn)題: ? 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(大多數(shù)成員) System.Globalization.CompareInfo(大多數(shù)成員) System.Array.Sort(對(duì)字符串?dāng)?shù)組進(jìn)行排序時(shí)) System.Collections.Generic.List<T>.Sort()(當(dāng)列表元素為字符串時(shí)) System.Collections.Generic.SortedDictionary<TKey,TValue>(當(dāng)鍵為字符串時(shí)) System.Collections.Generic.SortedList<TKey,TValue>(當(dāng)鍵為字符串時(shí)) System.Collections.Generic.SortedSet<T>(當(dāng)集包含字符串時(shí)) 這里面很多方法都是有多個(gè)重載方法的,而每個(gè)重載方法默認(rèn)當(dāng)前文化設(shè)置可能并不相同。因此大家在開(kāi)發(fā)的時(shí)候一定要注意使用,一不小心肯能就好引起一些奇怪的問(wèn)題,因此大家盡量自己手動(dòng)指定當(dāng)前文化設(shè)置。 下表列出一些方法其對(duì)應(yīng)的默認(rèn)行為。 注:當(dāng)然如果調(diào)用方提供顯式 CultureInfo 或 StringComparison 參數(shù),則該參數(shù)將優(yōu)先于任何默認(rèn)值。 最后總結(jié)一下 1、 IndexOf對(duì)于Empty字符查找會(huì)返回開(kāi)始查找索引startIndex,而不是我們想象中的-1; 2、 Unicode可忽略字符受StringComparison參數(shù)影響很大,會(huì)直接把相應(yīng)字符直接忽略掉 3、 .NET全球化進(jìn)程中,區(qū)域語(yǔ)言支持 (NLS)在向 Unicode 國(guó)際組件 (ICU)遷移是必然,因此我們?cè)谑褂孟嚓P(guān)方法時(shí)一定要小心 4、 如果可以盡量主動(dòng)顯示設(shè)置當(dāng)前文化區(qū)域設(shè)置 轉(zhuǎn)自https://www.cnblogs.com/hugogoos/p/18387624 該文章在 2024/12/6 10:49:18 編輯過(guò) |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |