【问题标题】:How to sort out numeric strings as numerics?如何将数字字符串排序为数字?
【发布时间】:2010-11-18 01:38:24
【问题描述】:

如果你有这样的字符串:

"file_0"
"file_1"
"file_2"
"file_3"
"file_4"
"file_5"
"file_6"
"file_11"

如何对它们进行排序,以使“file_11”不在“file_1”之后,而是在“file_6”之后,因为 11 > 6。

我是否必须解析字符串并将其转换为数字?

Win7 中的 Windows 资源管理器按照我想要的方式对文件进行分类。

【问题讨论】:

标签: c# .net string comparison


【解决方案1】:

我是否必须为此解析字符串并将其转换为数字?

基本上,是的;但 LINQ 可能会有所帮助:

var sorted = arr.OrderBy(s => int.Parse(s.Substring(5)));
foreach (string s in sorted) {
    Console.WriteLine(s);
}

【讨论】:

  • 谢谢马克。它肯定更干净。
  • 顺便说一句,马克,是否有“直到字符串结尾”传递给子字符串?否则我需要做一些计算,这会阻止我使用点符号,对吧?
  • 上面的重载“直到字符串的结尾”……5是开始索引。
【解决方案2】:

要处理任何格式的混合字符串和数字的排序,您可以使用这样的类将字符串拆分为字符串和数字组件并进行比较:

public class StringNum : IComparable<StringNum> {

   private List<string> _strings;
   private List<int> _numbers;

   public StringNum(string value) {
      _strings = new List<string>();
      _numbers = new List<int>();
      int pos = 0;
      bool number = false;
      while (pos < value.Length) {
         int len = 0;
         while (pos + len < value.Length && Char.IsDigit(value[pos+len]) == number) {
            len++;
         }
         if (number) {
            _numbers.Add(int.Parse(value.Substring(pos, len)));
         } else {
            _strings.Add(value.Substring(pos, len));
         }
         pos += len;
         number = !number;
      }
   }

   public int CompareTo(StringNum other) {
      int index = 0;
      while (index < _strings.Count && index < other._strings.Count) {
         int result = _strings[index].CompareTo(other._strings[index]);
         if (result != 0) return result;
         if (index < _numbers.Count && index < other._numbers.Count) {
            result = _numbers[index].CompareTo(other._numbers[index]);
            if (result != 0) return result;
         } else {
            return index == _numbers.Count && index == other._numbers.Count ? 0 : index == _numbers.Count ? -1 : 1;
         }
         index++;
      }
      return index == _strings.Count && index == other._strings.Count ? 0 : index == _strings.Count ? -1 : 1;
   }

}

例子:

List<string> items = new List<string> {
  "item_66b",
  "999",
  "item_5",
  "14",
  "file_14",
  "26",
  "file_2",
  "item_66a",
  "9",
  "file_10",
  "item_1",
  "file_1"
};

items.Sort((a,b)=>new StringNum(a).CompareTo(new StringNum(b)));

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

输出:

9
14
26
999
file_1
file_2
file_10
file_14
item_1
item_5
item_66a
item_66b

【讨论】:

  • @nawfal:如果你在没有比较器的情况下调用Sort,它将使用默认的字符串比较,你会得到不同的结果。此外,List&lt;T&gt; 类在 .NET 2.0 之前不存在。
  • @Guffa 不,我的意思是必须实现您编写的所有内容,包括像public class StringNum : IComparable&lt;StringNum&gt; 这样的接口。但我想最后一行代码items.Sort((a,b)=&gt;new StringNum(a).CompareTo(new StringNum(b))); 在.net 2.0 中不起作用.. 或者会吗?我只是用items.Sort() 代替它,并且让代码正常工作..!
  • @nawfal:lambda 表达式在 C# 2.0 中不起作用,因此您只需使用委托来编写它:items.Sort(delegate(string a, string b){ return new StringNum(a).CompareTo(new StringNum(b)); });
  • @Guffa 对此表示感谢。但是仅使用 .Sort() 对我有什么作用?你知道为什么吗?当我只是尝试在没有任何IComparable&lt;StringNum&gt; 的情况下执行.Sort() 时,代码甚至没有运行;给了我一个例外。请参阅此 linl,codedigest.com/Articles/CSHARP/84_Sorting_in_Generic_List.aspx 它有 : IComparable&lt;StringNum&gt; IComparable 示例,他们在其中执行了 .Sort() (该链接中的第一个方法)。所以我想我的应该可以工作
  • @nawfal:这个异常肯定有其他原因,StringNum 类与String 类没有关系,所以如果你不在Sort 中使用它调用它根本不会改变结果。在您正在阅读的示例中,他们有一个List&lt;Customer&gt;,其中Customer 类实现IComparable&lt;Customer&gt;,因此Sort 方法将使用它而不在调用中指定比较。这不适用于 List&lt;String&gt;,因为您无法更改 String 类的实现。
【解决方案3】:

您可以导入StrCmpLogicalW function 并使用它对字符串进行排序。这与 Explorer 本身用于文件名的函数完全相同。

不过,如果您不希望 P/Invoke 或在其他系统上保持兼容,这将无济于事。

【讨论】:

    【解决方案4】:

    以下基于 Joey 建议的代码适用于我(string[] 的扩展方法):

    public static void SortLogical(this string[] files)
    {
        Array.Sort<string>(files, new Comparison<string>(StrCmpLogicalW));
    }
    
    [DllImport("shlwapi.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
    private static extern int StrCmpLogicalW(String x, String y);
    

    【讨论】:

    • 这很好,但是你从哪里得到 shlwapi.dll?而且它似乎在windows7中不起作用
    • +1:剪切和粘贴对我来说非常有效,在 Windows-7 上也是如此。
    【解决方案5】:

    一个简单的方法是像这样填充数字部分:

    file_00001
    file_00002
    file_00010
    file_00011
    

    等等

    但这取决于知道数字部分可以取的最大值。

    【讨论】:

    • 谢谢。你如何在c#中填充数字?您的意思是解析并插入数字到字符串中?
    • 我认为 Mitch 的意思是:尽量不要一开始就使用该数据...更改您的输入以避免需要处理它。
    • 谢谢,我明白了。不幸的是,我无法控制文件名(在用户机器上):)
    【解决方案6】:

    我前段时间在一个项目中使用了以下方法。它不是特别有效,但是如果要排序的项目数量不是很大,那么它的性能就足以满足该用途。它的作用是将要比较的字符串拆分为'_' 字符上的数组,然后比较数组的每个元素。尝试将最后一个元素解析为 int,并在那里进行数值比较。

    如果输入字符串包含不同数量的元素,它也会提前退出(因此,如果您将“file_nbr_1”与“file_23”进行比较,它不会比较字符串的每个部分,而只是比较对完整字符串进行常规字符串比较):

    char[] splitChars = new char[] { '_' };
    string[] strings = new[] {
        "file_1",
        "file_8",
        "file_11",
        "file_2"
    };
    
    Array.Sort(strings, delegate(string x, string y)
    {
        // split the strings into arrays on each '_' character
        string[] xValues = x.Split(splitChars);
        string[] yValues = y.Split(splitChars);
    
        // if the arrays are of different lengths, just 
        //make a regular string comparison on the full values
        if (xValues.Length != yValues.Length)
        {
            return x.CompareTo(y);
        }
    
        // So, the arrays are of equal length, compare each element
        for (int i = 0; i < xValues.Length; i++)
        {
            if (i == xValues.Length - 1)
            {
                // we are looking at the last element of the arrays
    
                // first, try to parse the values as ints
                int xInt = 0;
                int yInt = 0;
                if (int.TryParse(xValues[i], out xInt) 
                    && int.TryParse(yValues[i], out yInt))
                {
                    // if parsing the values as ints was successful 
                    // for both values, make a numeric comparison 
                    // and return the result
                    return xInt.CompareTo(yInt);
                }
            }
    
            if (string.Compare(xValues[i], yValues[i], 
                StringComparison.InvariantCultureIgnoreCase) != 0)
            {
                break;
            }
        }
    
        return x.CompareTo(y);
    
    });
    

    【讨论】:

      猜你喜欢
      • 2012-01-20
      • 2021-11-22
      • 2015-09-28
      • 2017-03-04
      • 1970-01-01
      • 2016-02-14
      • 1970-01-01
      • 2011-02-10
      • 1970-01-01
      相关资源
      最近更新 更多