.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 轮:只看字母(忽略大小写和重音)
- 第 2 轮:看重音(没有重音的在前)
- 第 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 有三个缺点:
- 慢。它要做三轮比较,Ordinal 只比数字。
- 会"看不见"某些字符。比如软连字符这种不可见字符会被直接忽略,
"coop"和中间藏了一个不可见字符的"coop"会被判断为相等。如果用它做安全校验,可能被人绕过。 - 结果可能因系统而异。它依赖操作系统的语言库,不同版本可能有细微差别。
而程序内部的字符串(字典的键、文件名、配置项、网址)根本不需要"像人一样"排序,只需要快、准、永远不变。这正是 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" 相等。
需要知道的两个细节:
- 它一个字符对一个字符地转换。德语的
ß不会变成SS,所以"Straße"和"STRASSE"不相等。 - 它不受用户电脑设置影响。在土耳其语环境下,
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 = 转大写后按编号比,忽略大小写时的首选。
浙公网安备 33010602011771号