C# 数组。使用数字对字符串进行排序

C# 数组。使用数字对字符串进行排序,c#,C#,我有以下示例代码: List<string> test = new List<string>(); test.Add("Hello2"); test.Add("Hello1"); test.Add("Welcome2"); test.Add("World"); test.Add("Hello11"); test.Add("Hello10"); test.Add("Welcome0"); test.Add("World3"); test.Add("Hello100"); t

我有以下示例代码:

List<string> test = new List<string>();
test.Add("Hello2");
test.Add("Hello1");
test.Add("Welcome2");
test.Add("World");
test.Add("Hello11");
test.Add("Hello10");
test.Add("Welcome0");
test.Add("World3");
test.Add("Hello100");
test.Add("Hello20");
test.Add("Hello3");

test.Sort();
有没有办法对它们进行排序,以便字符串也具有正确的数字顺序? 如果字符串末尾没有数字,则该字符串将始终位于第一位-在字母顺序之后

预期产出:

"Hello1", 
"Hello2", 
"Hello3", 
"Hello10", 
"Hello11", 
"Hello20", 
"Hello100", 
"Welcome0", 
"Welcome2", 
"World", 
"World3"

以下是使用LINQ的一种可能方法:

 var orderedList = test
            .OrderBy(x => new string(x.Where(char.IsLetter).ToArray()))
            .ThenBy(x =>
            {
                int number;
                if (int.TryParse(new string(x.Where(char.IsDigit).ToArray()), out number))
                    return number;
                return -1;
            }).ToList();

您可以将LINQ与正则表达式结合使用,以确保只使用字符串末尾的数字进行二次排序

test
  .Select(t => new{match = Regex.Match(t, @"\d+$"), val = t})
  .Select(x => new{sortVal = x.match.Success
                                ?int.Parse(x.match.Value)
                                :-1,
                   val = x.val})
  .OrderBy(x => x.val)
  .ThenBy(x => x.sortVal)
  .Select(x => x.val)
  .ToList()
创建一个IComparer实现。与LINQ建议相比,这种方式的优势在于,您现在有了一个类,它可以传递给任何需要以这种方式排序的对象,而不是在其他位置重新创建LINQ查询

这是特定于从列表中调用排序的。如果要将其称为Array.Sort,请参阅版本2:

列表版本:

public class AlphaNumericComparer : IComparer<string>
    {
        public int Compare(string lhs, string rhs)
        {
            if (lhs == null)
            {
                return 0;
            }

            if (rhs == null)
            {
                return 0;
            }

            var s1Length = lhs.Length;
            var s2Length = rhs.Length;
            var s1Marker = 0;
            var s2Marker = 0;

            // Walk through two the strings with two markers.
            while (s1Marker < s1Length && s2Marker < s2Length)
            { 
                var ch1 = lhs[s1Marker];
                var ch2 = rhs[s2Marker];

                var s1Buffer = new char[s1Length];
                var loc1 = 0;
                var s2Buffer = new char[s2Length];
                var loc2 = 0;

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

                    if (s1Marker < s1Length)
                    {
                        ch1 = lhs[s1Marker];
                    }
                    else
                    {
                        break;
                    }
                } while (char.IsDigit(ch1) == char.IsDigit(s1Buffer[0]));

                do
                {
                    s2Buffer[loc2++] = ch2;
                    s2Marker++;

                    if (s2Marker < s2Length)
                    {
                        ch2 = rhs[s2Marker];
                    }
                    else
                    {
                        break;
                    }
                } while (char.IsDigit(ch2) == char.IsDigit(s2Buffer[0]));

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

                int result;

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

                if (result != 0)
                {
                    return result;
                }
            }
            return s1Length - s2Length;
        }
    }
Array.sort版本:

创建类:

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)
        {
            var ch1 = s1[marker1];
            var ch2 = s2[marker2];

            // Some buffers we can build up characters in for each chunk.
            var space1 = new char[len1];
            var loc1 = 0;
            var space2 = new char[len2];
            var 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.
            var str1 = new string(space1);
            var str2 = new string(space2);

            var result = 0;

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

            if (result != 0)
            {
                return result;
            }
        }
        return len1 - len2;
    }
}

Call like so:

This time test is an array instead of a list.
Array.sort(test, new AlphaNumericComparer())

只是随口说说而已,但如果你想让Hello1排在Hello10之前,最好先注入一个0,这样它就会是Hello01、Hello02等。Windows资源管理器按字母顺序对文件和文件夹进行排序时,也会采用相同的方式。可能需要查看字符串的最后2个字符,确定它们是否都是数字,如果不是,则以0开头。@sab669我不允许更改原始数据,它们对整个应用程序极为敏感。@C.J.您的欢迎。请注意,如果字符串开头或字母之间包含数字,则此操作将无法按预期进行。仅当所有字母后面都是数字时,此操作才有效。如果你想处理像这样的不同情况,请告诉我,我可以更新我的答案。+1因为我没有使用正则表达式来分割字符串和数字,而且完全依赖LINQ。@Habib:正则表达式有什么问题吗?@spender,我在发布此评论之前没有看到你的答案:,实际上我在尝试基于正则表达式的类似答案,结果变得很混乱。那只是我和REGEX。。。。
test.sort(new AlphaNumericComparer());

//RESULT
Hello1 
Hello2 
Hello3 
Hello10 
Hello11 
Hello20 
Hello100 
Welcome0 
Welcome2 
World 
World3 
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)
        {
            var ch1 = s1[marker1];
            var ch2 = s2[marker2];

            // Some buffers we can build up characters in for each chunk.
            var space1 = new char[len1];
            var loc1 = 0;
            var space2 = new char[len2];
            var 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.
            var str1 = new string(space1);
            var str2 = new string(space2);

            var result = 0;

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

            if (result != 0)
            {
                return result;
            }
        }
        return len1 - len2;
    }
}

Call like so:

This time test is an array instead of a list.
Array.sort(test, new AlphaNumericComparer())