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

我有一个字符串,其中包含以句点分隔的数字。 当我排序时它看起来像这样,因为它是一个字符串:(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. 使用“分割”function可获取各个数字
  2. 将值放入对象中
  3. 对对象排序

如果重复现有function,我宁愿避免所有这些工作。 .net框架中的方法是否已经执行此操作?

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

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

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

看那个..刚发现这个问题 。

承包商:

 class StringNumberComparer : IComparer { 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 testStrings = new List{ "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 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 

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

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

  class MyStringComparer : IComparer { 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()); 

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

您正在寻找的是自然排序顺序, Jeff Atwood对此大加赞赏,并且链接到不同语言的实现 。 .NET Framework不包含实现。

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

虽然你可能能够使用正则表达式或Linq避免过多的轮子重新发明,但事实并非如此。 请记住,使用内置的东西来滚动自己的内容会花费大量的计算成本。

试试这个:

 List 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组合器使比较成为lambda。

用’。’拆分每个字符串,遍历组件并用数字进行比较。

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

  int Compare(string a, string b) { string[] aParts = a.Split('.'); string[] bParts = b.Split('.'); /// if A has more components than B, it must be larger. if (aParts.Length != bParts.Length) return (aParts.Length > bParts.Length) ? 1 : -1; int result = 0; /// iterate through each numerical component for (int i = 0; i < aParts.Length; i++) if ( (result = int.Parse(aParts[i]).CompareTo(int.Parse(bParts[i]))) !=0 ) return result; /// all components are equal. return 0; } public string[] sort() { /// initialize test data string l = "3.9.5.2.1.1\n" + "3.9.5.2.1.10\n" + "3.9.5.2.1.11\n" + "3.9.5.2.1.12\n" + "3.9.5.2.1.2\n" + "3.9.5.2.1.3\n" + "3.9.5.2.1.4\n"; /// split the large string into lines string[] arr = l.Split(new char[] { '\n' },StringSplitOptions.RemoveEmptyEntries); /// create a list from the array List strings = new List(arr); /// sort using our custom sort routine strings.Sort(Compare); /// concatenate the list back to an array. return strings.ToArray(); } 

您可以使用David Koelle提供的令人敬畏的AlphanumComparator Alphanum自然排序算法 。

码:

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

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

 AlphanumComparator : IComparer 

 public int Compare(string x, string y) 

除了像Jon提到的那样实现你自己的IComparer之外,如果在数组上调用ToList(),你可以调用.Sort()方法并传入一个比较两个值的函数参数,如下所示: http:// msdn .microsoft.com / EN-US /库/ w56d4y5z.aspx