【问题标题】:Why does "-less" sort after "hello" instead of before it?为什么“-less”在“hello”之后而不是在它之前排序?
【发布时间】:2011-04-10 11:31:15
【问题描述】:

我看到一些使用 CaseInsensitiveComparer.DefaultInvariant 的非常奇怪的排序行为。以连字符“-”开头的单词最终会被排序,就好像连字符不存在一样,而不是像其他标点符号那样被排序在实际字母前面。

所以给定 { "hello", ".net", "-less"} 我最终得到 {".net", "hello", "-less" } 而不是预期的 {"-less", " .net”,“你好”}。

或者,用测试用例来表述:

[TestMethod]
public void TestMethod1()
{
    var rg = new String[] { 
        "x", "z", "y", "-less", ".net", "- more", "a", "b"
    };

    Array.Sort(rg, CaseInsensitiveComparer.DefaultInvariant);

    Assert.AreEqual(
        "- more,-less,.net,a,b,x,y,z", 
        String.Join(",", rg)
    );
}

... 像这样失败:

Assert.AreEqual failed. 
Expected:<- more,-less,.net,a,b,x,y,z>. 
Actual:  <- more,.net,a,b,-less,x,y,z>.

有什么想法吗?

编辑:

看起来,默认情况下,.NET 在对字符串进行排序时会做一些花哨的事情,这会导致前导连字符被排序到奇怪的位置,以便合作和合作排序在一起。因此,如果您希望前导连字符以其他标点符号结尾并以其他标点符号开头,您必须告诉它不要:

Array.Sort(rg, (a, b) => String.CompareOrdinal(a, b));

【问题讨论】:

  • 不加空格可能被认为类似于负号
  • 如果每个问题都可以表示为一个单元测试。
  • 我不知道点 '.',但看起来 StringComparer.InvariantCulture 在进行比较之前只是吃掉了所有的破折号 '-'。

标签: c# .net sorting


【解决方案1】:

比较过程使用 CultureInfo.InvariantCulture 来确定排序顺序和大小写规则。字符串比较可能有不同的结果,具体取决于文化。有关特定于文化的比较的更多信息,请参阅 System.Globalization 命名空间和编码和本地化。 From here.

有趣的部分:

单词排序对字符串执行文化敏感的比较,其中某些非字母数字 Unicode 字符可能具有分配给它们的特殊权重。例如,连字符 (-) 可能分配给它的权重非常小,因此“coop”和“co-op”在排序列表中彼此相邻出现。 From here.

【讨论】:

  • 那么OP可以解决如下混淆吗? “字符串排序还执行文化敏感比较。它类似于单词排序,除了没有特殊情况,所有非字母数字符号都在所有字母数字 Unicode 字符之前。两个字符串可以通过调用使用字符串排序规则进行比较CompareInfo.Compare 方法重载有一个 options 参数,该参数提供了 CompareOptions.StringSort 的值。请注意,这是 .NET Framework 提供的使用字符串排序规则比较两个字符串的唯一方法。"
  • +1,很好的答案。通过考虑许多不同类型的破折号来激发你的大脑,你键盘上的那个总是错误的:en.wikipedia.org/wiki/Dash
【解决方案2】:

要以您需要的方式对字符串进行排序,您必须创建一个使用Compareinfo class 比较字符串的比较器类。此类允许您指定各种比较方法,最符合您需要的是 OrdinalIgnoreCase。

来自 MSDN:

忽略的搜索值

比较操作,例如那些 由 IndexOf 或 LastIndexOf 方法,可以产生 如果值为 搜索被忽略。搜索 如果值为空,则忽略该值 字符串 (""),一个字符或字符串 由具有代码的字符组成 未考虑的点 因为比较而操作 选项,或带有代码点的值 没有语言意义的。 如果 IndexOf 的搜索值 方法是一个空字符串,对于 例如,返回值为零。

注意
在可能的情况下,应用程序 应该使用字符串比较方法 接受一个 CompareOptions 值 指定比较类型 预期的。作为基本规则, 面向用户的比较是最好的 通过使用语言服务 选项(使用当前文化), 而安全比较应该 指定 Ordinal 或 OrdinalIgnoreCase。指定 Ordinal 或 OrdinalIgnoreCase。

我已经修改了你的测试用例,这个执行正确:

public class MyComparer:Comparer<string>
{
    private readonly CompareInfo compareInfo;

    public MyComparer()
    {
        compareInfo = CompareInfo.GetCompareInfo(CultureInfo.InvariantCulture.Name);
    }

    public override int Compare(string x, string y)
    {
        return compareInfo.Compare(x, y, CompareOptions.OrdinalIgnoreCase);
    }
}

public class Class1
{
    [Test]
    public void TestMethod1()
    {
        var rg = new String[] { 
    "x", "z", "y", "-less", ".net", "- more", "a", "b"
};

        Array.Sort(rg, new MyComparer());

        Assert.AreEqual(
            "- more,-less,.net,a,b,x,y,z",
            String.Join(",", rg)
        );


    }
}

【讨论】:

    【解决方案3】:

    我的猜测是,在一个字母之前的一个破折号会被忽略,以便进行排序。当您对单词列表进行排序时,您希望“inter-nation”和“international”彼此相邻,不是吗?另一方面,破折号本身被认为是重要的。

    【讨论】:

    • 不是真的 - 我希望(并期望)根据嵌入的非 alpha 字符在 ASCII 字符集中的位置对其进行排序。你是说根据这个比较器,“国际”和“国际”是一样的吗?
    【解决方案4】:

    排序顺序取决于文化,因此您不能假设字符会按 ASCII 顺序排序。

    http://msdn.microsoft.com/en-us/library/a7zyyk0c.aspx

    在您的示例中,“h”(U+0048)在“dash”(U+2013)之前,因此“hello”将出现在“-less”之前。 “。” (U+002E) 在两者之前,所以“.net”首先出现。

    【讨论】:

    • 解释错误。尝试对“aa”、“-bb”和“cc”进行排序,您会发现 unicode 顺序在这里并不重要。比较器更有可能“吃掉”前导点和破折号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-02
    • 2016-02-03
    • 1970-01-01
    相关资源
    最近更新 更多