【问题标题】:sorting when name includes letters and numeric digits当名称包含字母和数字时排序
【发布时间】:2013-05-24 16:03:57
【问题描述】:

我有以下数组

[0] = GB_22_T0001.jpg
[1] = GB_22_T0002.jpg
[2] = GB_22_T0003.jpg
[3] = GB_22_T0006.jpg
[4] = GB_22_T0007.jpg
[5] = GB_22_T0008.jpg
[6] = GB_22_T0009.jpg
[7] = GB_22_T00010.jpg
[8] = GB_22_T00011.jpg
[9] = GB_22_T00012.jpg
[10] = GB_22_T00013.jpg

我已将这些项目放入列表框中,并注意到“GB_22_T00010”直接出现在“GB_22_T0001”之后而不是“GB_22_T0002”

似乎是 c# 的一个常见问题,但找不到该问题的常见答案。

我尝试使用 Array.sort(data) 对数组进行排序,还尝试了 LinQ 的 OrderBy 方法,但都没有帮助。

谁有解决办法?

【问题讨论】:

  • 您存储的实际物品是什么?如果每个都已经是一个对象,只需向其中添加一个变量来存储最后一个数字部分,然后使用 LINQ 对其进行排序。
  • 这是预期的,因为您正在按字符串排序。如果您想进行混合比较,那么 SO 提供的#2“相关”问题的答案是:Sorting an array of folder names like Windows Explorer (Numerically and Alphabetically)(很惊讶您说您找不到一个共同的答案,因为它就在那里。)
  • 看起来文件名的人犯了一个愚蠢的错误。 T 后面的数字都以 000 开头,这让我觉得他们意识到了这一点,但是当数字增长到更多位数时,他们不会使用该填充!我希望看到... 0008 0009 0010 0011 0012 ...

标签: c# arrays linq sorting


【解决方案1】:

这是我对同时包含字母和数字字符的字符串进行排序的代码。

首先,这个扩展方法:

public static IEnumerable<string> AlphanumericSort(this IEnumerable<string> me)
{
    return me.OrderBy(x => Regex.Replace(x, @"\d+", m => m.Value.PadLeft(50, '0')));
}

然后,只需像这样在代码中的任何位置使用它:

List<string> test = new List<string>() { "The 1st", "The 12th", "The 2nd" };
test = test.AlphanumericSort();

它是如何工作的?通过用零替换:

  Original  | Regex Replace |      The      |   Returned
    List    | Apply PadLeft |    Sorting    |     List
            |               |               |
 "The 1st"  |  "The 001st"  |  "The 001st"  |  "The 1st"
 "The 12th" |  "The 012th"  |  "The 002nd"  |  "The 2nd"
 "The 2nd"  |  "The 002nd"  |  "The 012th"  |  "The 12th"

适用于多个数字:

 Alphabetical Sorting | Alphanumeric Sorting
                      |
 "Page 21, Line 42"   | "Page 3, Line 7"
 "Page 21, Line 5"    | "Page 3, Line 32"
 "Page 3, Line 32"    | "Page 21, Line 5"
 "Page 3, Line 7"     | "Page 21, Line 42"

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    GB_22_T0001 是字符串而不是数字。所以它是按字典顺序而不是按数字排序的。所以你需要将parse字符串的一部分转换成int

    var ordered = array.Select(Str => new { Str, Parts=Str.Split('_') })
                       .OrderBy(x => int.Parse(x.Parts.Last().Substring(1))) 
                       .Select(x => x.Str);
    

    Split('_') 将字符串拆分为分隔符 _ 上的子字符串。最后一个子字符串包含您的数值。然后我使用String.Substring 只取int.Parse 的数字部分(删除开头的T)。此整数用于Enumerable.OrderBy。最后一步是只选择字符串而不是匿名类型。

    编辑:这里是支持Paths的版本:

    var ordered = array.Select(str => { 
        string fileName = Path.GetFileNameWithoutExtension(str);
        string[] parts =  fileName.Split('_');
        int number = int.Parse(parts.Last().Substring(1));
        return new{ str, fileName, parts, number };
     })
    .OrderBy(x => x.number)
    .Select(x => x.str);
    

    【讨论】:

    • 对不起,我忘了在最后添加 .jpg。此代码是否可以与字符串末尾的 .jpg 一起使用?
    • @user1293053:编辑了我的答案。
    【解决方案3】:

    Windows 有一个内置的比较函数,您可以使用它来比较这样的字符串(字符串和数字的混合):StrCmpLogicalW

    您可以将它用作 IComparer 的核心来进行排序。

    这篇博文有很多关于这个的细节:http://gregbeech.com/blog/natural-sort-order-of-strings-and-files

    效果很好。

    编辑:我根据上面的博客使用的实现:

    public sealed class NaturalStringComparer : IComparer<string>
    {
      public static readonly NaturalStringComparer Default = new NaturalStringComparer();
    
      public int Compare(string x, string y)
      {
        return SafeNativeMethods.StrCmpLogicalW(x, y);
      }
    }
    
    [SuppressUnmanagedCodeSecurity]
    internal static class SafeNativeMethods
    {
      [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
      public static extern int StrCmpLogicalW(string psz1, string psz2);
    }
    

    然后使用 LINQ:

    var sortedItems = items.OrderBy(i => i, new NaturalStringComparer());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-14
      • 2018-06-24
      • 2016-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-26
      相关资源
      最近更新 更多