包含字母和数字的名称排序

5

我有以下数组

[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对其进行排序。 - Sean Cogan
这是预期的,因为您正在按字符串排序。如果您想进行混合比较,则SO提供的第二个“相关”问题有答案:Sorting an array of folder names like Windows Explorer (Numerically and Alphabetically)(很惊讶您说找不到共同的答案,因为它就在那里)。 - Raymond Chen
2
看起来制作文件名的人犯了一个愚蠢的错误。跟在 T 后面的数字都以 000 开头,这让我觉得他们意识到了这一点,但是当数字增长到更多位数时,他们没有使用这种填充!我希望能看到...0008 0009 0010 0011 0012... - Timothy Shields
3个回答

5
这是我用来排序既包含字母又包含数字字符的字符串的代码。
首先,这是一个扩展方法:
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 是一个字符串而不是数字。因此,它按字典顺序排序而不是数字顺序。因此,您需要将字符串的一部分解析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仅获取数值部分(删除起始的T)以供int.Parse使用。这个整数用于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 后能正常工作吗? - Sahil

2

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());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接