【问题标题】:sort string-numbers [duplicate]排序字符串数字[重复]
【发布时间】:2012-04-16 20:26:27
【问题描述】:

可能重复:
Natural Sort Order in C#

我有一个列表,里面有很多数字。 但是由于一些额外的字母,它们被保存为字符串。

我的列表如下所示:

1
10
11
11a
11b
12
2
20
21a
21c
A1
A2
...

但它应该看起来像这样

1
2
10
11a
11b
...
A1
A2
...

如何对我的列表进行排序以获得此结果?

【问题讨论】:

  • 将“数字”分解为组件,然后按其排序。
  • 是的,自然排序是您所追求的。这是 Jon 所说的重复。一篇关于它的好文章zootfroot.blogspot.com.au/2009/09/…
  • 我确认@Jason Jong 指出的解决方案运行良好,而下面接受的解决方案没有按预期排序。

标签: c# string list sorting


【解决方案1】:

按照之前的 cmets,我还将实现一个自定义 IComparer<T> 类。据我所知,项目的结构要么是一个数字,要么是一个数字后跟一个字母的组合。如果是这种情况,下面的IComparer<T> 实现应该可以工作。

public class CustomComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        var regex = new Regex("^(d+)");

        // run the regex on both strings
        var xRegexResult = regex.Match(x);
        var yRegexResult = regex.Match(y);

        // check if they are both numbers
        if (xRegexResult.Success && yRegexResult.Success)
        {
            return int.Parse(xRegexResult.Groups[1].Value).CompareTo(int.Parse(yRegexResult.Groups[1].Value));
        }

        // otherwise return as string comparison
        return x.CompareTo(y);
    }
}

有了这个IComparer&lt;T&gt;,你就可以对string列表进行排序了

var myComparer = new CustomComparer();
myListOfStrings.Sort(myComparer);

已通过以下项目进行了测试:

2, 1, 4d, 4e, 4c, 4a, 4b, A1, 20, B2, A2, a3, 5, 6, 4f, 1a

并给出结果:

1, 1a, 2, 20, 4a, 4b, 4c, 4d, 4e, 4f, 5, 6, A1, A2, a3, B2

【讨论】:

  • 这很酷,但它不能正确处理版本号之类的东西...... 1.5.2、1.9.9、1.10.17 奇怪的是,Windows 资源管理器正确排序这些东西......但显然比较器不可用于任何其他 C# 代码,以便重现文件资源管理器在按文件名排序时显示的相同文件名顺序。令人沮丧。
  • 我相信正则表达式应该是^(\\d+)。更好的是,正则表达式可能只是 (\\d+) 来处理 Region 1, Region 10, Region 2 以及 1, 10, 2
【解决方案2】:

由于这包括许多字符串操作、正则表达式等,我认为它不是一种有效的算法,但它似乎有效。

List<string> list1 = new List<string>() { "11c22", "1", "10", "11", "11a", "11b", "12", "2", "20", "21a", "21c", "A1", "A2" };
List<string> list2 = new List<string>() { "File (5).txt", "File (1).txt", "File (10).txt", "File (100).txt", "File (2).txt" };
var sortedList1 = NaturalSort(list1).ToArray();
var sortedList2 = NaturalSort(list2).ToArray();

public static IEnumerable<string> NaturalSort(IEnumerable<string> list)
{
    int maxLen = list.Select(s => s.Length).Max();
    Func<string, char> PaddingChar = s => char.IsDigit(s[0]) ? ' ' : char.MaxValue;

    return list
            .Select(s =>
                new
                {
                    OrgStr = s,
                    SortStr = Regex.Replace(s, @"(\d+)|(\D+)", m => m.Value.PadLeft(maxLen, PaddingChar(m.Value)))
                })
            .OrderBy(x => x.SortStr)
            .Select(x => x.OrgStr);
}

【讨论】:

  • 希望看到实际处理版本号的版本...有多个数字要按顺序排列:2.5.7、10.3.2、2.18.3 等。
  • 这很好用!谢谢!
  • 当我问了一个类似的问题时被提醒到这个线程并且这个方法完美无缺
  • 这项工作很棒!
【解决方案3】:

好吧,您需要从每个字符串中提取数字,然后根据数字列表作为键对字符串列表进行排序。分两步完成。

要从每个字符串中提取数字,我认为最简单的方法是使用正则表达式 - 查找 (\d+) 的匹配项(如果您有负数或小数,则必须使用不同的正则表达式)。假设您在一个名为 ExtractNumber

的函数中执行了此操作

现在您可以使用一些创造性的 LINQ 进行排序,如下所示:

strings.Select(s=>new { key=ExtractNumber(s), value=s }) // Create a key-value pair
       .OrderBy(p=>p.key)                                // Sort by key
       .Select(p=>p.Value);                              // Extract the values

【讨论】:

  • 这看起来像是一个使用 LINQ 的优雅解决方案,但是当迭代到项目 A1A2 时会发生什么?
  • int.Parse((new Regex(@"(?
【解决方案4】:

我对 C# 比较陌生,但这里有一个我很欣赏的 Java 解决方案:您需要分两步进行,首先定义一个自定义的 IComparer,然后在调用 sort 方法时使用它。因此,您应该能够执行以下操作:

public class MyListSorter : IComparer<MyObject>
{
  public int Compare(MyObject obj1, MyObject obj2)
  {
    if ( !Char.IsNumber(obj1) && Char.IsNumber(obj2) )
    {
       return 0;
    }
    else if ( Char.IsNumber(obj1) && !Char.IsNumber(obj2) )
    {
      return 1;
    }
    else
    {
      return obj2.CompareTo(obj1);
    }
  }
}

然后

myObjectList.Sort(new MyListSorter());

更多关于 IComparer 的信息:http://support.microsoft.com/kb/320727

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-06
    • 2011-10-14
    • 1970-01-01
    相关资源
    最近更新 更多