如何使用C#以数字方式对值进行排序?

时间:2010-12-08 16:41:38

标签: c# .net string sorting numbers

我有一个字符串,其中包含以句点分隔的数字。当我排序它看起来像这样,因为它是一个字符串:(ascii char order)

3.9.5.2.1.1
3.9.5.2.1.10
3.9.5.2.1.11
3.9.5.2.1.12
3.9.5.2.1.2
3.9.5.2.1.3
3.9.5.2.1.4

我希望它像这样排序:(按数字顺序)

3.9.5.2.1.1
3.9.5.2.1.2
3.9.5.2.1.3
...
3.9.5.2.1.9
3.9.5.2.1.10
3.9.5.2.1.11
3.9.5.2.1.12

我知道我可以:

  1. 使用拆分功能获取个别数字
  2. 将值放入对象
  3. 对象排序
  4. 如果重复现有功能,我宁愿避免所有这些工作。 .net框架中的方法是否已经执行此操作?

9 个答案:

答案 0 :(得分:4)

这是我的工作解决方案,它也处理不正确格式的字符串(例如包含文本)。

这个想法是获取两个字符串中的第一个数字并比较这些数字。如果匹配,请继续下一个号码。如果他们不这样做,我们就有了胜利者。如果这些数字不是一个数字,那么对该部分进行字符串比较,但尚未比较。

通过更改确定下一个数字的方式,可以很容易地使比较器与自然排序顺序完全兼容。

看那......刚刚找到this question

比较者:

class StringNumberComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        int compareResult;
        int xIndex = 0, yIndex = 0;
        int xIndexLast = 0, yIndexLast = 0;
        int xNumber, yNumber;
        int xLength = x.Length;
        int yLength = y.Length;

        do
        {
            bool xHasNextNumber = TryGetNextNumber(x, ref xIndex, out xNumber);
            bool yHasNextNumber = TryGetNextNumber(y, ref yIndex, out yNumber);

            if (!(xHasNextNumber && yHasNextNumber))
            {
                // At least one the strings has either no more number or contains non-numeric chars
                // In this case do a string comparison of that last part
                return x.Substring(xIndexLast).CompareTo(y.Substring(yIndexLast));
            }

            xIndexLast = xIndex;
            yIndexLast = yIndex;

            compareResult = xNumber.CompareTo(yNumber);
        }
        while (compareResult == 0
            && xIndex < xLength
            && yIndex < yLength);

        return compareResult;
    }

    private bool TryGetNextNumber(string text, ref int startIndex, out int number)
    {
        number = 0;

        int pos = text.IndexOf('.', startIndex);
        if (pos < 0) pos = text.Length;

        if (!int.TryParse(text.Substring(startIndex, pos - startIndex), out number))
            return false;

        startIndex = pos + 1;

        return true;
    }
}

<强>用法:

public static void Main()
{
    var comparer = new StringNumberComparer();

    List<string> testStrings = new List<string>{
        "3.9.5.2.1.1",
        "3.9.5.2.1.10",
        "3.9.5.2.1.11",
        "3.9.test2",
        "3.9.test",
        "3.9.5.2.1.12",
        "3.9.5.2.1.2",
        "blabla",
        "....",
        "3.9.5.2.1.3",
        "3.9.5.2.1.4"};

    testStrings.Sort(comparer);

    DumpArray(testStrings);

    Console.Read();
}

private static void DumpArray(List<string> values)
{
    foreach (string value in values)
    {
        Console.WriteLine(value);
    }
}

<强>输出:

....
3.9.5.2.1.1
3.9.5.2.1.2
3.9.5.2.1.3
3.9.5.2.1.4
3.9.5.2.1.10
3.9.5.2.1.11
3.9.5.2.1.12
3.9.test
3.9.test2
blabla

答案 1 :(得分:3)

不,我不相信框架中有任何自动执行此操作的内容。您可以编写自己的IComparer<string>实现,其中不会进行任何拆分,而是迭代两个字符串,只比较所需的数量(即只解析每个字符串的第一个数字,然后继续,如果有必要等)但我怀疑这将是非常繁琐。它还需要假设“1.2.3.4.5”与“1.3”相比如何(即值包含不同数量的数字)。

答案 2 :(得分:2)

由于您要对字符串进行的比较与.Net中通常比较字符串的方式不同,您必须使用自定义字符串字符串比较器

 class MyStringComparer : IComparer<string>
        {
            public int Compare(string x, string y)
            {
                // your comparison logic
                // split the string using '.' separator
                // parse each string item in split array into an int
                // compare parsed integers from left to right
            }
        }

然后您可以在OrderBy和Sort

等方法中使用比较器
var sorted = lst.OrderBy(s => s, new MyStringComparer());

lst.Sort(new MyStringComparer());

这将为您提供所需的结果。如果没有,那么只需调整比较器。

答案 3 :(得分:2)

您要寻找的是自然排序顺序Jeff Atwood bloged about it and has links to implementations in different languages。 .NET Framework不包含实现。

答案 4 :(得分:0)

您是否可以使用0将字段填充到前面相同的长度?如果是这样,那么你可以在字符串上使用直接的词典排序。否则,框架内置的这种方法不会自动执行此操作。如果填充不是一种选择,则必须实现自己的IComparer<string>

答案 5 :(得分:0)

不是真的,尽管你可以使用Regexes或Linq来避免过多的轮胎重新发明。请记住,使用内置的东西进行计算会花费你很多钱。

试试这个:

List<string> myList = GetNumberStrings();

myList.Select(s=>s.Split('.')).ToArray().
   .Sort((a,b)=>RecursiveCompare(a,b))
   .Select(a=>a.Aggregate(new StringBuilder(),
      (s,sb)=>sb.Append(s).Append(".")).Remove(sb.Length-1, 1).ToString())
   .ToList();

...

public int RecursiveCompare(string[] a, string[] b)
{
    return RecursiveCompare(a,b,0)
}

public int RecursiveCompare(string[] a, string[] b, int index)
{
    return index == a.Length || index == b.Length 
        ? 0 
        : a[index] < b[index] 
            ? -1 
            : a[index] > b[index] 
                ? 1 
                : RecursiveCompare(a,b, index++);
}

不是最紧凑的,但它应该可以工作,你可以使用y-combinator使比较成为lambda。

答案 6 :(得分:0)

将每个字符串拆分为“。”,迭代组件并以数字方式对它们进行比较。

此代码还假设组件的数量是显着的(字符串'1.1.1'将大于'2.1'。这可以通过更改{{1}中的第一个if语句来调整。下面的方法。

Compare

答案 7 :(得分:0)

您可以使用David Koelle的精彩AlphanumComparator Alphanum natural sort algorithm

代码:

OrderBy(o => o.MyString, new AlphanumComparator())

如果您要使用C#版本,请将其更改为:

AlphanumComparator : IComparer<string>

public int Compare(string x, string y)

答案 8 :(得分:-1)

除了像Jon所提到的那样实现你自己的IComparer之外,如果在数组上调用ToList(),你可以调用.Sort()方法并传入一个比较两个值的函数参数,如下所示:{{ 3}}