我正在寻找一种像Windows资源管理器那样的自然排序技术.

例如,如果我有字母数字数组-

var array = new[]{"B01 002", "B01 0010", "01", "B01 001", "B10 001", "B01 01", "1", "B1 001", "B02 001", "A1"};

I am expecting this to be sorted in the below Order-
[01, 1, A1, B01 001, B01 01, B01 002, B01 0010, B1 001, B02 001, B10 001]

这正是Windows资源管理器的工作方式-

enter image description here

我已经try 了以下帖子中的解决方案-

对所采取的方法没有其他限制.可以使用LINQ、正则表达式、扩展或接口方法来复制"shlwapi.dll"完成的排序顺序

推荐答案

我建议将字符串拆分成块,每个块要么全是数字,要么不是数字:"B01 001" -> {"B", "01", " ", "001"}. 然后比较这些块(比较两个全数字块是特例).

Code:(Fiddle)

public sealed class NaturalComparer : IComparer<string> {

  private static int CompareChunks(string x, string y) {
    if (x[0] >= '0' && x[0] <= '9' && y[0] >= '0' && y[0] <= '9') {
      string tx = x.TrimStart('0');
      string ty = y.TrimStart('0');

      int result = tx.Length.CompareTo(ty.Length);

      if (result != 0)
        return result;

      result = tx.CompareTo(ty);

      if (result != 0)
        return result;
    }
    
    return string.Compare(x, y);
  }

  public int Compare(string? x, string? y) {
    if (ReferenceEquals(x, y))
      return 0;
    if (x is null)
      return -1;
    if (y is null)
      return +1;

    var itemsX = Regex
      .Split(x, "([0-9]+)")
      .Where(item => !string.IsNullOrEmpty(item))
      .ToList();

    var itemsY = Regex
      .Split(y, "([0-9]+)")
      .Where(item => !string.IsNullOrEmpty(item))
      .ToList();

    for (int i = 0; i < Math.Min(itemsX.Count, itemsY.Count); ++i) {
      int result = CompareChunks(itemsX[i], itemsY[i]);

      if (result != 0)
        return result;
    }

    return itemsX.Count.CompareTo(itemsY.Count);
  }
}

Demo:

string[] demo = new string[] {
    "B01 002", 
    "B01 0010", 
    "01", 
    "B01 001", 
    "B10 001", 
    "B01 01", 
    "1", 
    "B1 001", 
    "B02 001", 
    "A1"
};

Array.Sort(demo, new NaturalComparer());

Console.WriteLine(string.Join(Environment.NewLine, demo));

Output:

01
1
A1
B01 001
B01 01
B01 002
B01 0010
B1 001
B02 001
B10 001

Csharp相关问答推荐

如果没有中间变量,可空引用类型将无法工作

如何将ref T*重新解释为ref nint?

C#使用属性和值将JSON转换为XML

dotnet集合中内部数组的局部变量副本的用途是什么?'

Select Many和默认IfEmpty内部Select Many错误工作

如何在C#中将对象[*,*]直接转换为字符串[*,*]?

为什么SignalR在每个Blazor服务器应用程序启动时最多启动8个服务器?

如何测量在使用UTF8而不是C#中的UTF16编码字符串时内存使用量的增长

从.Net 6 DLL注册和检索COM对象(Typelib导出:类型库未注册.(异常来自HRESULT:0x80131165))

Azure函数中实体框架核心的依赖注入

HttpClient 415不支持的媒体类型错误

Blazor Web App WASM的两个独立项目令人困惑

正在try 从Blazor中的API读取JSON

什么类型的对象存储在大对象堆(LOH)中

如何在同一成员上组合[JsonPropertyName]和[ObservableProperty]?

为什么ReadOnlySpan;T&>没有Slice(...)的重载接受Range实例的?

如何在发布NuGet包之前设置命名空间?

Linq SELECT的多条指令

C#中的逻辑运算符用作单词';is';and';and';

无法将.Net Framework 4.8.1升级到.Net 7