【问题标题】:Unexpected behavior when sorting strings with letters and dashes使用字母和破折号对字符串进行排序时出现意外行为
【发布时间】:2014-03-20 03:23:58
【问题描述】:

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

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

为什么当字符串包含字母与仅包含数字时,排序在字符串上的行为会有所不同?

提前致谢。

【问题讨论】:

  • 请不要在问题标题中包含有关所用语言的信息,除非没有它就没有意义。标记用于此目的。
  • 这很奇怪。直到我自己运行它,我才相信它......
  • 你见过this吗??

标签: c# sorting


【解决方案1】:

这是来自MSDN的评论:

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

所以看起来您正在经历这种可忽略的字符大小写。如果我们假设-符号的权重比较小,那么排序的结果是这样的。

第一种情况:

660616280000
660616280100
6606162801000
6606162801040

第二种情况:

66061628000A
660616280100A
660616280104A
66061628010A 

有道理

【讨论】:

  • 很好地解释了文化敏感性如何影响这种情况。
  • 我不确定它实际上是一个 ignorable 字符,因为我相信它只是一个普通的 ASCII 连字符。我怀疑这是“重量轻”方面起作用 - 有关详细信息,请参阅我编辑的答案。
  • @JonSkeet,是的,我明白了。但仍不清楚连字符何时会影响文化敏感排序。小重量仍然是重量,它应该在某个地方发挥作用。你知道这样的例子吗?
  • @Andrei:我怀疑这意味着“a-b”将出现在“a--b”之前,而如果字符完全忽略,它们将被平均排序。只是猜测:)
【解决方案2】:

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

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

那么CompareOptions的页面包括:

.NET Framework 使用三种不同的排序方式:单词排序、字符串排序和序数排序。 Word 排序对字符串执行区分区域性的比较。某些非字母数字字符可能具有分配给它们的特殊权重。例如,连字符(“-”)可能分配给它的权重非常小,因此“coop”和“co-op”在排序列表中彼此相邻出现。字符串排序类似于单词排序,只是没有特殊情况。因此,所有非字母数字符号都位于所有字母数字字符之前。序数排序根据字符串中每个元素的 Unicode 值比较字符串。

(“Small weight”与 Andrei 的回答中引用的“ignored”并不完全相同,但此处的效果相似。)

如果您指定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"));

【讨论】:

  • 我认为如果没有解释排序的不同之处,这个答案并不完整。说如何修复它很好,但很高兴知道为什么原版失败了,这就是问题所在(尽管安德烈已经解释过)。
  • 我同意,Andrei 和 Jon 的答案结合起来确实是答案,希望我能同时选择。
  • @BBauer42,我认为你应该选择 Jon's,所以当其他人遇到同样的问题时,他们会看到如何解决。不是每个人都关心解释,但每个人都关心解决方案。
  • @Andrei:希望我的编辑现在也包含足够的解释:)
  • 感谢 Jon 的更新版本。一个更完整的答案。
猜你喜欢
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
  • 1970-01-01
  • 2022-06-12
  • 1970-01-01
  • 2021-02-21
  • 2016-10-28
相关资源
最近更新 更多