如何将数字字符串整理为数字?

时间:2009-08-09 08:35:59

标签: c# .net string comparison

如果您有以下字符串:

"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资源管理器按照我想要的方式对文件进行排序。

6 个答案:

答案 0 :(得分:12)

  

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

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

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

答案 1 :(得分:10)

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

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

答案 2 :(得分:9)

您可以导入StrCmpLogicalW function并使用它来排序字符串。这与Explorer本身用于文件名的功能完全相同。

如果您不想要P / Invoke或在其他系统上保持兼容,

将无法帮助您。

答案 3 :(得分:6)

以下基于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);

答案 4 :(得分:2)

一种简单的方法是填充数字部分,如下所示:

file_00001
file_00002
file_00010
file_00011

但是这可以知道数字部分可以采用的最大值。

答案 5 :(得分:2)

我刚才在项目中使用了以下方法。它不是特别有效,但如果要排序的项目数量不是很大,那么它的表现就足够了。它的作用是将字符串拆分为比较'_'字符上的数组,然后比较数组的每个元素。尝试将最后一个元素解析为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);

});