【问题标题】:Array.Sort for strings with numbers [duplicate]Array.Sort 用于带有数字的字符串 [重复]
【发布时间】:2014-04-01 20:44:27
【问题描述】:

我有以下示例代码:

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();

但发生的情况是,test.Sort 会将数组排序为:

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

有没有办法对它们进行排序,以便string 也有正确的数字顺序? (如果string 的末尾没有数字,那string 将始终排在最前面 - 在字母顺序之后)

预期输出:

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

【问题讨论】:

  • 只是吐口水,但如果你想让 "Hello1" 出现在 "Hello10" 之前,你最好注入一个 0,所以它是 "Hello01"、"Hello02" 等。 Windows 资源管理器按字母顺序对文件和文件夹进行排序。可能想查看字符串的最后 2 个字符并确定它们是否是 both 数字,如果不是,则以 0 开头。
  • @sab669 我不允许更改原始数据,它们对整个应用程序非常敏感。

标签: c#


【解决方案1】:

这是使用 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();

【讨论】:

  • @C.J.您的欢迎。请注意,如果您的字符串在开头或字母之间包含数字,则这将无法按预期工作。仅当数字出现在所有字母之后时才有效。如果您想处理这样的不同情况,请告诉我,我可以更新我的答案。
  • +1 表示不使用 Regex 来拆分字符串和数字,而纯粹依赖 LINQ。
  • @Habib:你的正则表达式有什么问题?
  • @spender,在发布此评论之前我没有看到您的答案 :),实际上我正在尝试基于 Regex 的类似答案并且它变得混乱。那只是我和 REGEX....
【解决方案2】:

创建一个IComparer&lt;string&gt; 实现。与LINQ 建议相比,这样做的好处是您现在拥有一个可以传递给任何需要以这种方式排序的类,而不是在其他位置重新创建该 linq 查询。

这是特定于您从 LIST 调用排序的。如果你想叫它Array.Sort(),请看第二版:

列表版本:

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;
        }
    }

这样调用:

test.sort(new AlphaNumericComparer());

//RESULT
Hello1 
Hello2 
Hello3 
Hello10 
Hello11 
Hello20 
Hello100 
Welcome0 
Welcome2 
World 
World3 

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())

【讨论】:

    【解决方案3】:

    您可以将 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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-18
      相关资源
      最近更新 更多