使用字母和短划线排序字符串时出现意外行为

时间:2014-02-19 16:32:10

标签: c# sorting

如果我有一些包含所有数字和破折号的字符串列表,它们将按升序排序:

s = s.OrderBy(t => t).ToList();

66-0616280-000
66-0616280-100
66-06162801000
66-06162801040

这是预期的。

但是,如果字符串包含字母,则排序有些出乎意料。例如,这里是相同的字符串列表,其中尾随A代替0,是的,它已排序:

66-0616280-00A
66-0616280100A
66-0616280104A
66-0616280-10A

我本以为他们会这样排序:

66-0616280-00A
66-0616280-10A
66-0616280100A
66-0616280104A

为什么排序在包含字母的字符串与仅包含数字时的字符串表现不同?

提前致谢。

2 个答案:

答案 0 :(得分:12)

这是因为默认的StringComparer是文化敏感的。据我所知,Comparer<string>.Default代表使用当前文化的string.CompareTo(string)

  

此方法使用当前文化执行单词(区分大小写和文化敏感)比较。有关单词,字符串和序数排序的更多信息,请参阅System.Globalization.CompareOptions

然后CompareOptions的页面包含:

  

.NET Framework使用三种不同的排序方式:单词排序,字符串排序和顺序排序。单词排序执行文化敏感的字符串比较。某些非字母数字字符可能会分配特殊权重。例如,连字符(“ - ”)可能具有非常小的权重,因此“coop”和“co-op”在排序列表中彼此相邻。字符串排序类似于单词排序,除了没有特殊情况。因此,所有非字母数字符号都在所有字母数字字符之前。序数排序根据字符串中每个元素的Unicode值比较字符串。

(“小重量”与安德烈答案中引用的“忽略”并不完全相同,但效果在这里类似。)

如果您指定StringComparer.Ordinal,则会得到以下结果:

66-0616280-00A
66-0616280-10A
66-0616280100A
66-0616280104A

将其指定为OrderBy的第二个参数:

s = s.OrderBy(t => t, StringComparer.Ordinal).ToList();

你可以在这里看到不同之处:

Console.WriteLine(Comparer<string>.Default.Compare
    ("66-0616280104A", "66-0616280-10A"));
Console.WriteLine(StringComparer.Ordinal.Compare
    ("66-0616280104A", "66-0616280-10A"));

答案 1 :(得分:5)

以下是来自MSDN的评论:

  

字符集包括可忽略的字符。比较(字符串,   String)方法在执行时不考虑这样的字符   文化敏感的比较。例如,如果以下代码是   在.NET Framework 4或更高版本上运行,这是一种对文化敏感的比较   具有“动物”的“动物”(使用软连字符或U + 00AD)表示   这两个字符串是等价的。

所以看起来你正在经历这种可忽略的角色案例。如果我们假设-符号的权重非常小,则排序结果如下所示。

第一种情况:

660616280000
660616280100
6606162801000
6606162801040

第二种情况:

66061628000A
660616280100A
660616280104A
66061628010A 

哪个有意义