.NET 字符串比较:Ordinal、InvariantCulture 和 Culture

程序内部用的字符串,用 Ordinal;展示给人看的文字,用 CurrentCulture。

记住这句话,90% 的场景就不会选错。下面解释为什么。

一、为什么字符串比较有好几种?

因为"两个字符串谁排前面"有两种完全不同的标准:

  • 计算机的标准:每个字符都有一个编号,按编号大小比。
  • 人的标准:像查字典一样,按字母顺序比。

这两种标准经常给出相反的结果。StringComparer 就是让你选择用哪种标准。

二、Ordinal:按编号比

每个字符在计算机里都有一个编号:

数字 0~9  = 48~57
大写 A~Z  = 65~90
小写 a~z  = 97~122
é         = 233

Ordinal 只看编号大小,从左往右逐个比,遇到第一个不同的字符就定胜负。

所以它的排序是:数字 → 大写字母 → 小写字母 → 带重音的字母。

例子:"a" vs "B"

  • a 的编号是 97,B 的编号是 66
  • 97 更大,所以 "a" 排在后面

三、InvariantCulture:像查英文字典

它分三轮比较,前一轮分出胜负,就不再往下比:

  1. 第 1 轮:只看字母(忽略大小写和重音)
  2. 第 2 轮:看重音(没有重音的在前)
  3. 第 3 轮:看大小写(小写在前)

例子:

比较 结果 原因
"a" vs "B" a 在前 第 1 轮:字典里 a 在 b 前面
"resume" vs "résumé" resume 在前 第 1 轮打平,第 2 轮:没重音的在前
"apple" vs "Apple" apple 在前 前两轮打平,第 3 轮:小写在前

四、同一对单词,两种规则结果相反

"apple" vs "Zebra":

规则 怎么比 谁在前
Ordinal a=97,Z=90,Z 编号更小 Zebra
InvariantCulture 字典里 a 在 z 前面 apple

对一组单词排序,差别更明显:

Ordinal:          Apple, Zebra, apple, banana
InvariantCulture: apple, Apple, banana, Zebra

第二种才是人觉得"正常"的顺序。

五、既然 InvariantCulture 更像人,为什么程序里推荐 Ordinal?

因为 InvariantCulture 有三个缺点:

  1. 慢。它要做三轮比较,Ordinal 只比数字。
  2. 会"看不见"某些字符。比如软连字符这种不可见字符会被直接忽略,"coop" 和中间藏了一个不可见字符的 "co­op" 会被判断为相等。如果用它做安全校验,可能被人绕过。
  3. 结果可能因系统而异。它依赖操作系统的语言库,不同版本可能有细微差别。

而程序内部的字符串(字典的键、文件名、配置项、网址)根本不需要"像人一样"排序,只需要快、准、永远不变。这正是 Ordinal 的优点。

六、Culture 是什么?

Culture(区域性)就是**"某个国家或地区的书写习惯"**,比如 zh-CN(中国大陆中文)、en-US(美国英语)、de-DE(德国德语)。

它不只影响字符串比较,还影响很多东西:

影响什么 美国 en-US 德国 de-DE
数字 1,234.56 1.234,56
货币 $1,234.56 1.234,56 €
日期 10/13/2026 13.10.2026

一个常见的坑:在德国电脑上,double.Parse("1.5") 得到的是 15,而不是 1.5,因为德国人用点号作千位分隔符。

三种 Culture 的区别:

  • CurrentCulture:跟着用户电脑的设置走。中国用户看到中国格式,德国用户看到德国格式。
  • InvariantCulture:固定不变的"通用格式"(基于英语),在任何电脑上都一样。适合保存文件、写配置。
  • 具体某个 Culture:比如 new CultureInfo("de-DE"),你指定用哪国规则就用哪国。

七、OrdinalIgnoreCase 是怎么忽略大小写的?

简单理解:把字母都转成大写,再按编号比。

所以 "Hello" 和 "HELLO" 相等。

需要知道的两个细节:

  1. 它一个字符对一个字符地转换。德语的 ß 不会变成 SS,所以 "Straße" 和 "STRASSE" 不相等。
  2. 它不受用户电脑设置影响。在土耳其语环境下,i 的大写是 İ(带点),如果用 CurrentCultureIgnoreCase,"file" 和 "FILE" 会被判断为不相等。而 OrdinalIgnoreCase 永远判断为相等。

所以不要写 a.ToUpper() == b.ToUpper(),直接用 OrdinalIgnoreCase,更快也更安全。

八、怎么选?一张表搞定

场景 用哪个
字典的键、标识符、Linux 文件名 Ordinal
HTTP 头、Windows 文件名、环境变量 OrdinalIgnoreCase
给用户看的列表排序 CurrentCulture
给用户用的搜索框 CurrentCultureIgnoreCase
需要按字典排序,但要求每台电脑结果一样 InvariantCulture
把数字、日期保存到文件或 JSON CultureInfo.InvariantCulture(用于格式化)

九、动手试试

新建控制台项目,把下面代码粘贴到 Program.cs,运行 dotnet run:

using System.Globalization;

string[] words = { "banana", "Apple", "apple", "Zebra" };

// 1. 两种规则排序对比
Console.WriteLine("Ordinal:          " + string.Join(", ", words.OrderBy(w => w, StringComparer.Ordinal)));
Console.WriteLine("InvariantCulture: " + string.Join(", ", words.OrderBy(w => w, StringComparer.InvariantCulture)));

// 2. 忽略大小写的字典
var dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase) { ["Name"] = 1 };
Console.WriteLine("找到 name 吗?" + dict.ContainsKey("name"));   // True

// 3. 土耳其语陷阱
CultureInfo.CurrentCulture = new CultureInfo("tr-TR");
Console.WriteLine("CurrentCultureIgnoreCase: " + StringComparer.CurrentCultureIgnoreCase.Equals("file", "FILE")); // False
Console.WriteLine("OrdinalIgnoreCase:        " + StringComparer.OrdinalIgnoreCase.Equals("file", "FILE"));        // True

// 4. 德国格式解析陷阱
Console.WriteLine("德国解析 1.5 = " + double.Parse("1.5", new CultureInfo("de-DE")));   // 15

总结

  • Ordinal = 按编号比,快、准、永远不变,适合程序内部。
  • InvariantCulture = 按字典比(字母 → 重音 → 大小写),适合需要固定结果的人类可读排序。
  • CurrentCulture = 按用户所在地区的习惯比,适合展示给用户。
  • Culture 不只管比较,还管数字、货币、日期的格式。
  • OrdinalIgnoreCase = 转大写后按编号比,忽略大小写时的首选。
posted @ 2026-10-14 08:21  无风听海  阅读(8)  评论(0)    收藏  举报