116

我正在尝试对作为字符串的数字数组进行排序,我希望它们按数字排序。

问题是我无法将数字转换为 int

这是代码:

string[] things= new string[] { "105", "101", "102", "103", "90" };

foreach (var thing in things.OrderBy(x => x))
{
    Console.WriteLine(thing);
}

输出

101, 102, 103, 105, 90

我想要:

90, 101, 102, 103, 105

编辑: 输出不能090, 101, 102...

将代码示例更新为“ things”而不是“ sizes”。数组可以是这样的:

string[] things= new string[] { "paul", "bob", "lauren", "007", "90" };

这意味着它需要按字母顺序和数字排序:

007, 90, bob, lauren, paul
4

21 回答 21

115

将自定义比较器传递给 OrderBy。Enumerable.OrderBy将让您指定您喜欢的任何比较器。

这是一种方法:

void Main()
{
    string[] things = new string[] { "paul", "bob", "lauren", "007", "90", "101"};

    foreach (var thing in things.OrderBy(x => x, new SemiNumericComparer()))
    {    
        Console.WriteLine(thing);
    }
}


public class SemiNumericComparer: IComparer<string>
{
    /// <summary>
    /// Method to determine if a string is a number
    /// </summary>
    /// <param name="value">String to test</param>
    /// <returns>True if numeric</returns>
    public static bool IsNumeric(string value)
    {
        return int.TryParse(value, out _);
    }

    /// <inheritdoc />
    public int Compare(string s1, string s2)
    {
        const int S1GreaterThanS2 = 1;
        const int S2GreaterThanS1 = -1;

        var IsNumeric1 = IsNumeric(s1);
        var IsNumeric2 = IsNumeric(s2);

        if (IsNumeric1 && IsNumeric2)
        {
            var i1 = Convert.ToInt32(s1);
            var i2 = Convert.ToInt32(s2);

            if (i1 > i2)
            {
                return S1GreaterThanS2;
            }

            if (i1 < i2)
            {
                return S2GreaterThanS1;
            }

            return 0;
        }

        if (IsNumeric1)
        {
            return S2GreaterThanS1;
        }

        if (IsNumeric2)
        {
            return S1GreaterThanS2;
        }

        return string.Compare(s1, s2, true, CultureInfo.InvariantCulture);
    }
}
于 2011-06-18T16:18:04.143 回答
110

只需用零填充相同的长度:

int maxlen = sizes.Max(x => x.Length);
var result = sizes.OrderBy(x => x.PadLeft(maxlen, '0'));
于 2011-06-18T13:56:31.860 回答
89

值是一个字符串

List = List.OrderBy(c => c.Value.Length).ThenBy(c => c.Value).ToList();

作品

于 2015-08-12T16:22:13.273 回答
76

还有,这个怎么样...

string[] sizes = new string[] { "105", "101", "102", "103", "90" };

var size = from x in sizes
           orderby x.Length, x
           select x;

foreach (var p in size)
{
    Console.WriteLine(p);
}
于 2011-06-18T14:33:51.917 回答
18

Windows中有一个本机函数StrCmpLogicalW,可以将字符串中的数字作为数字而不是字母进行比较。制作一个调用该函数并将其用于比较的比较器很容易。

public class StrCmpLogicalComparer : Comparer<string>
{
    [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
    private static extern int StrCmpLogicalW(string x, string y);

    public override int Compare(string x, string y)
    {
        return StrCmpLogicalW(x, y);
    }
}

它甚至适用于同时包含文本和数字的字符串。这是一个示例程序,它将显示默认排序和StrCmpLogicalW排序之间的差异

class Program
{
    static void Main()
    {
        List<string> items = new List<string>()
        {
            "Example1.txt", "Example2.txt", "Example3.txt", "Example4.txt", "Example5.txt", "Example6.txt", "Example7.txt", "Example8.txt", "Example9.txt", "Example10.txt",
            "Example11.txt", "Example12.txt", "Example13.txt", "Example14.txt", "Example15.txt", "Example16.txt", "Example17.txt", "Example18.txt", "Example19.txt", "Example20.txt"
        };

        items.Sort();

        foreach (var item in items)
        {
            Console.WriteLine(item);
        }

        Console.WriteLine();

        items.Sort(new StrCmpLogicalComparer());

        foreach (var item in items)
        {
            Console.WriteLine(item);
        }
        Console.ReadLine();
    }
}

哪个输出

Example1.txt
Example10.txt
Example11.txt
Example12.txt
Example13.txt
Example14.txt
Example15.txt
Example16.txt
Example17.txt
Example18.txt
Example19.txt
Example2.txt
Example20.txt
Example3.txt
Example4.txt
Example5.txt
Example6.txt
Example7.txt
Example8.txt
Example9.txt

Example1.txt
Example2.txt
Example3.txt
Example4.txt
Example5.txt
Example6.txt
Example7.txt
Example8.txt
Example9.txt
Example10.txt
Example11.txt
Example12.txt
Example13.txt
Example14.txt
Example15.txt
Example16.txt
Example17.txt
Example18.txt
Example19.txt
Example20.txt
于 2016-12-03T03:41:25.263 回答
5

试试这个

sizes.OrderBy(x => Convert.ToInt32(x)).ToList<string>();

注意:当所有字符串都可以转换为 int 时,这将很有帮助.....

于 2011-06-18T13:38:47.980 回答
5

您说您无法将数字转换为 int,因为数组可以包含无法转换为 int 的元素,但尝试没有害处:

string[] things = new string[] { "105", "101", "102", "103", "90", "paul", "bob", "lauren", "007", "90" };
Array.Sort(things, CompareThings);

foreach (var thing in things)
    Debug.WriteLine(thing);

然后像这样比较:

private static int CompareThings(string x, string y)
{
    int intX, intY;
    if (int.TryParse(x, out intX) && int.TryParse(y, out intY))
        return intX.CompareTo(intY);

    return x.CompareTo(y);
}

输出:007、90、90、101、102、103、105、鲍勃、劳伦、保罗

于 2014-05-15T17:34:37.487 回答
5

如果字符串中有一些数字,我想这会更好。希望它会有所帮助。

PS:我不确定性能或复杂的字符串值,但它工作得很好,如下所示:

lorem ipsum
lorem ipsum 1
lorem ipsum 2
lorem ipsum 3
...
lorem ipsum 20
lorem ipsum 21

public class SemiNumericComparer : IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        int s1r, s2r;
        var s1n = IsNumeric(s1, out s1r);
        var s2n = IsNumeric(s2, out s2r);

        if (s1n && s2n) return s1r - s2r;
        else if (s1n) return -1;
        else if (s2n) return 1;

        var num1 = Regex.Match(s1, @"\d+$");
        var num2 = Regex.Match(s2, @"\d+$");

        var onlyString1 = s1.Remove(num1.Index, num1.Length);
        var onlyString2 = s2.Remove(num2.Index, num2.Length);

        if (onlyString1 == onlyString2)
        {
            if (num1.Success && num2.Success) return Convert.ToInt32(num1.Value) - Convert.ToInt32(num2.Value);
            else if (num1.Success) return 1;
            else if (num2.Success) return -1;
        }

        return string.Compare(s1, s2, true);
    }

    public bool IsNumeric(string value, out int result)
    {
        return int.TryParse(value, out result);
    }
}
于 2015-10-25T14:19:19.140 回答
4

该站点讨论了字母数字排序,并将以逻辑意义而不是 ASCII 意义对数字进行排序。它还考虑了它周围的 alpha:

http://www.dotnetperls.com/alphanumeric-sorting

例子:

  • C:/TestB/333.jpg
  • 11
  • C:/TestB/33.jpg
  • 1
  • C:/TestA/111.jpg
  • 111F
  • C:/TestA/11.jpg
  • 2
  • C:/TestA/1.jpg
  • 111D
  • 22
  • 111Z
  • C:/TestB/03.jpg

  • 1
  • 2
  • 11
  • 22
  • 111D
  • 111F
  • 111Z
  • C:/TestA/1.jpg
  • C:/TestA/11.jpg
  • C:/TestA/111.jpg
  • C:/TestB/03.jpg
  • C:/TestB/33.jpg
  • C:/TestB/333.jpg

代码如下:

class Program
{
    static void Main(string[] args)
    {
        var arr = new string[]
        {
           "C:/TestB/333.jpg",
           "11",
           "C:/TestB/33.jpg",
           "1",
           "C:/TestA/111.jpg",
           "111F",
           "C:/TestA/11.jpg",
           "2",
           "C:/TestA/1.jpg",
           "111D",
           "22",
           "111Z",
           "C:/TestB/03.jpg"
        };
        Array.Sort(arr, new AlphaNumericComparer());
        foreach(var e in arr) {
            Console.WriteLine(e);
        }
    }
}

public class AlphaNumericComparer : IComparer
{
    public int Compare(object x, object y)
    {
        string s1 = x as string;
        if (s1 == null)
        {
            return 0;
        }
        string s2 = y as string;
        if (s2 == null)
        {
            return 0;
        }

        int len1 = s1.Length;
        int len2 = s2.Length;
        int marker1 = 0;
        int marker2 = 0;

        // Walk through two the strings with two markers.
        while (marker1 < len1 && marker2 < len2)
        {
            char ch1 = s1[marker1];
            char ch2 = s2[marker2];

            // Some buffers we can build up characters in for each chunk.
            char[] space1 = new char[len1];
            int loc1 = 0;
            char[] space2 = new char[len2];
            int loc2 = 0;

            // Walk through all following characters that are digits or
            // characters in BOTH strings starting at the appropriate marker.
            // Collect char arrays.
            do
            {
                space1[loc1++] = ch1;
                marker1++;

                if (marker1 < len1)
                {
                    ch1 = s1[marker1];
                }
                else
                {
                    break;
                }
            } while (char.IsDigit(ch1) == char.IsDigit(space1[0]));

            do
            {
                space2[loc2++] = ch2;
                marker2++;

                if (marker2 < len2)
                {
                    ch2 = s2[marker2];
                }
                else
                {
                    break;
                }
            } while (char.IsDigit(ch2) == char.IsDigit(space2[0]));

            // If we have collected numbers, compare them numerically.
            // Otherwise, if we have strings, compare them alphabetically.
            string str1 = new string(space1);
            string str2 = new string(space2);

            int result;

            if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
            {
                int thisNumericChunk = int.Parse(str1);
                int thatNumericChunk = int.Parse(str2);
                result = thisNumericChunk.CompareTo(thatNumericChunk);
            }
            else
            {
                result = str1.CompareTo(str2);
            }

            if (result != 0)
            {
                return result;
            }
        }
        return len1 - len2;
    }
}
于 2014-09-29T22:40:53.867 回答
3

这似乎是一个奇怪的要求,值得一个奇怪的解决方案:

string[] sizes = new string[] { "105", "101", "102", "103", "90" };

foreach (var size in sizes.OrderBy(x => {
    double sum = 0;
    int position = 0;
    foreach (char c in x.ToCharArray().Reverse()) {
        sum += (c - 48) * (int)(Math.Pow(10,position));
        position++;
    }
    return sum;
}))

{
    Console.WriteLine(size);
}
于 2011-06-18T13:45:50.167 回答
3

Jeff Paulsen 给出的答案是正确的,但Comprarer可以简化为:

public class SemiNumericComparer: IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        if (IsNumeric(s1) && IsNumeric(s2))
          return Convert.ToInt32(s1) - Convert.ToInt32(s2)

        if (IsNumeric(s1) && !IsNumeric(s2))
            return -1;

        if (!IsNumeric(s1) && IsNumeric(s2))
            return 1;

        return string.Compare(s1, s2, true);
    }

    public static bool IsNumeric(object value)
    {
        int result;
        return Int32.TryParse(value, out result);
    }
}

这是有效的,因为唯一检查结果的事情是Comparer结果是否更大、更小或等于零。可以简单地从另一个值中减去值,而不必处理返回值。

此外,该IsNumeric方法不必使用try-block 并且可以受益于TryParse.

对于那些不确定的人:此比较器将对值进行排序,非数值总是附加到列表的末尾。如果一开始就想要它们,则if必须交换第二个和第三个块。

于 2014-11-26T20:58:27.467 回答
2
public class NaturalSort: IComparer<string>
{
          [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
          public static extern int StrCmpLogicalW(string x, string y);

          public int Compare(string x, string y)
          {
                 return StrCmpLogicalW(x, y);
          }
}

arr = arr.OrderBy(x => x, new NaturalSort()).ToArray();

我需要它的原因是要在文件名以数字开头的目录中归档:

public static FileInfo[] GetFiles(string path)
{
  return new DirectoryInfo(path).GetFiles()
                                .OrderBy(x => x.Name, new NaturalSort())
                                .ToArray();
}
于 2017-04-15T00:08:56.310 回答
1

试试这个 :

string[] things= new string[] { "105", "101", "102", "103", "90" };

int tmpNumber;

foreach (var thing in (things.Where(xx => int.TryParse(xx, out tmpNumber)).OrderBy(xx =>     int.Parse(xx))).Concat(things.Where(xx => !int.TryParse(xx, out tmpNumber)).OrderBy(xx => xx)))
{
    Console.WriteLine(thing);
}
于 2014-05-15T16:24:59.560 回答
1

扩展 Jeff Paulsen 的答案。我想确保字符串中有多少个数字或字符组并不重要:

public class SemiNumericComparer : IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        if (int.TryParse(s1, out var i1) && int.TryParse(s2, out var i2))
        {
            if (i1 > i2)
            {
                return 1;
            }

            if (i1 < i2)
            {
                return -1;
            }

            if (i1 == i2)
            {
                return 0;
            }
        }

        var text1 = SplitCharsAndNums(s1);
        var text2 = SplitCharsAndNums(s2);

        if (text1.Length > 1 && text2.Length > 1)
        {

            for (var i = 0; i < Math.Max(text1.Length, text2.Length); i++)
            {

                if (text1[i] != null && text2[i] != null)
                {
                    var pos = Compare(text1[i], text2[i]);
                    if (pos != 0)
                    {
                        return pos;
                    }
                }
                else
                {
                    //text1[i] is null there for the string is shorter and comes before a longer string.
                    if (text1[i] == null)
                    {
                        return -1;
                    }
                    if (text2[i] == null)
                    {
                        return 1;
                    }
                }
            }
        }

        return string.Compare(s1, s2, true);
    }

    private string[] SplitCharsAndNums(string text)
    {
        var sb = new StringBuilder();
        for (var i = 0; i < text.Length - 1; i++)
        {
            if ((!char.IsDigit(text[i]) && char.IsDigit(text[i + 1])) ||
                (char.IsDigit(text[i]) && !char.IsDigit(text[i + 1])))
            {
                sb.Append(text[i]);
                sb.Append(" ");
            }
            else
            {
                sb.Append(text[i]);
            }
        }

        sb.Append(text[text.Length - 1]);

        return sb.ToString().Split(' ');
    }
}

在修改它以处理文件名之后,我还从SO Page中获取了 SplitCharsAndNums。

于 2019-01-18T10:33:58.703 回答
1

短 IComparer 类的示例。

  1. 如果两个字符串参数都可以转换为整数,则将参数解析为整数并进行比较
  2. 如果只有一个参数可以转换为整数,则整数优先(具有较低的值)并插入到字符串之前。
  3. 如果没有一个参数可以转换为整数,则使用普通字符串比较。

代码:

public class CompareIntegerStrings : IComparer<string>
    {
        public int Compare(string x, string y)
        {
            if (int.TryParse(x, out int xOut) && int.TryParse(y, out int yOut))
                return xOut.CompareTo(yOut);
            else if (int.TryParse(x, out _))
                return -1;
            else if (int.TryParse(y, out _))
                return 1;
            else
                return x.CompareTo(y);
    
        }
    }

在这个例子中

List<string> intStrings = new List<string> { "01","0022","abba", "11", "deep purple", "02", };
List<string> orderedIntStrings = intStrings.OrderBy(i=>i,new CompareIntegerStrings()).ToList();

有序列表 orderedIntString 是 { "01","02","11","0022","abba","deep Purple"}。

于 2021-08-10T08:55:02.360 回答
0
Try this out..  



  string[] things = new string[] { "paul", "bob", "lauren", "007", "90", "-10" };

        List<int> num = new List<int>();
        List<string> str = new List<string>();
        for (int i = 0; i < things.Count(); i++)
        {

            int result;
            if (int.TryParse(things[i], out result))
            {
                num.Add(result);
            }
            else
            {
                str.Add(things[i]);
            }


        }

现在对列表进行排序并将它们合并回来......

        var strsort = from s in str
                      orderby s.Length
                      select s;

        var numsort = from n in num
                     orderby n
                     select n;

        for (int i = 0; i < things.Count(); i++)
        {

         if(i < numsort.Count())
             things[i] = numsort.ElementAt(i).ToString();
             else
             things[i] = strsort.ElementAt(i - numsort.Count());               
               }

我只是试图在这个有趣的问题上做出贡献......

于 2011-06-18T18:43:24.173 回答
0

我首选的解决方案(如果所有字符串都是数字):

// Order by numerical order: (Assertion: all things are numeric strings only) 
foreach (var thing in things.OrderBy(int.Parse))
{
    Console.Writeline(thing);
}
于 2015-04-24T06:50:21.047 回答
0
public class Test
{
    public void TestMethod()
    {
        List<string> buyersList = new List<string>() { "5", "10", "1", "str", "3", "string" };
        List<string> soretedBuyersList = null;

        soretedBuyersList = new List<string>(SortedList(buyersList));
    }

    public List<string> SortedList(List<string> unsoredList)
    {
        return unsoredList.OrderBy(o => o, new SortNumericComparer()).ToList();
    }
}

   public class SortNumericComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        int xInt = 0;
        int yInt = 0;
        int result = -1;

        if (!int.TryParse(x, out xInt))
        {
            result = 1;
        }

        if(int.TryParse(y, out yInt))
        {
            if(result == -1)
            {
                result = xInt - yInt;
            }
        }
        else if(result == 1)
        {
             result = string.Compare(x, y, true);
        }

        return result;
    }
}
于 2015-10-01T16:55:37.250 回答
0

使用 Regex.Replace非常简单而高效。请注意,数字“3”必须是等于或大于最长字符串的数字,因此对于其他任何人,请根据需要增加。

using System.Text.RegularExpressions;

string[] things = new string[] { "105", "101", "102", "103", "90" };

foreach (var thing in things.OrderBy(x => Regex.Replace(x, @"\d+", i => 
i.Value.PadLeft(3, '0'))))
{
    Console.WriteLine(thing);
}
于 2021-08-20T20:08:45.483 回答
-1

尽管这是一个老问题,但我想给出一个解决方案:

string[] things= new string[] { "105", "101", "102", "103", "90" };

foreach (var thing in things.OrderBy(x => Int32.Parse(x) )
{
    Console.WriteLine(thing);
}

哇,很简单吧?:D

于 2014-06-19T14:53:51.020 回答
-1
namespace X
{
    public class Utils
    {
        public class StrCmpLogicalComparer : IComparer<Projects.Sample>
        {
            [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
            private static extern int StrCmpLogicalW(string x, string y);


            public int Compare(Projects.Sample x, Projects.Sample y)
            {
                string[] ls1 = x.sample_name.Split("_");
                string[] ls2 = y.sample_name.Split("_");
                string s1 = ls1[0];
                string s2 = ls2[0];
                return StrCmpLogicalW(s1, s2);
            }
        }

    }
}
于 2018-09-12T11:38:14.243 回答