【问题标题】:Comparing strings with hyphen将字符串与连字符进行比较
【发布时间】:2012-12-03 09:13:29
【问题描述】:

我发现了很多关于此的帖子,但未能将它们与我的案例相匹配。

我的列表中有一些字符串

List<string> list = new List<string>{ "100-1", "100-11", "100-3", "100-20" }

我使用以下代码对从this location挑选的代码进行排序

void Main()
{
    string[] things= new string[] { "100-1", "100-11", "100-3", "100-20" };

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

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

        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)
    {
        try {
            int i = Convert.ToInt32(value.ToString());
            return true; 
        }
        catch (FormatException) {
            return false;
        }
    }
}

我的输出是100-1, 100-11, 100-20, 100-3

我相信它会将- 视为decimal 并比较这些值。其实我期待的结果是 100-1, 100-3, 100-11, 100-20。 我只是想知道它实际上是在什么基础上进行排序的。任何帮助表示赞赏。即使我希望它以不同的方式对待100-2100-20

只是在运行中,我在 Infragistic 控制网格中看到,在其中排序会产生与我在这里预期的结果相同的结果。

编辑

我在列表中还有许多其他字符串值,有些是整数、双精度值等等。连字符只是这里提到的一个案例。

【问题讨论】:

  • 如果列表中的某些元素数字(不带破折号),您将为每个元素调用两次Convert.ToInt32。而且使用try-catch 并不是最好的选择。请改用int.TryParse 方法。它会检查它是否是一个数字,在一次操作中给你这个数字。而且您不需要捕获异常。并且您可以指定s1s2 必须遵守的一些“数字样式”才能成为“好”整数。
  • 它将它们排序为字符串。想象一下,如果字母的数字:1 => a; 3=> c;和 11 => aa。然后它将排序为 a, aa, c 而不是 a, c, aa。您必须在比较器中将带有连字符或任何其他分隔符的字符串视为特殊情况才能获得所需的结果。

标签: c# sorting compare hyphen string-comparison


【解决方案1】:
var sorted = things.Select(s => s.Split('-'))
                .OrderBy(x => double.Parse(x[0]))
                .ThenBy(x => double.Parse(x[1]))
                .Select(x=>String.Join("-",x))
                .ToList();

【讨论】:

  • 这是否可行,因为用户可以使用不同的特殊字符而不是-??这只是一个疑问:)
  • @rapsalands 您可以使用任何字符 s.Split(new char[]{'-','#'}) 拆分字符串
【解决方案2】:

这应该可以按预期工作:

string[] things= new string[] { "100-1", "100-11", "100-3", "100-20" };
IEnumerable<string> ordered = things
            .Select(s => new
            {
                str = s,
                firstPart = s.Split('-').ElementAtOrDefault(0),
                secondPart = s.Split('-').ElementAtOrDefault(1)
            })
            .OrderBy(x => int.Parse(x.firstPart))
            .ThenBy(x => int.Parse(x.firstPart))
            .Select(x => x.str);

foreach (string s in ordered)
    Console.WriteLine(s);

虽然它假定您的数据是严格的,否则您可以接受例外情况,例如int.Parse(x.firstPart)

演示http://ideone.com/UJ5Yt4

【讨论】:

  • 这是否可行,因为用户可以使用不同的特殊字符而不是-??这只是一个疑问:)
  • @rapsalands:您可以使用this overload 来使用多个分隔符。但是如果你的输入是任意的,你就不能使用 split。但后来出了点问题。
【解决方案3】:

如果您想按第二个数字(连字符之后)对项目进行排序,您需要将字符串解析为一个数字,然后使用它进行排序。你可以试试:

string[] things = new string[] { "100-1", "100-11", "100-3", "100-20" };
var test = things.OrderBy(r => int.Parse(r.Split('-')[1])).ToArray();

您当前的代码无法正常工作的原因可能是它无法将字符串 100- 解析为整数值,而您的函数 IsNumeric 返回 false。

【讨论】:

  • +1...是的...IsNumeric 返回错误。但即使它将值作为字符串,那么它在什么基础上给我这个输出?另请参阅已编辑的帖子,其中我提到我有许多其他类型的字符串(int,double as strings)以及这种类型。这样做是否可行,因为用户还可以输入连字符以外的特殊字符??
  • @rapsalands 你的代码总是落入string.Compare(s1, s2, true);,因为123-45 不是一个数字。你得到一个正常的字符串排序。
最近更新 更多