分享一个 Windows 资源管理器的文件名排序规则

using System.Runtime.InteropServices;

/// <summary>
/// Windows 资源管理器的文件名排序规则(允许在非 Windows 平台使用)
/// <para>调用 Windows API(StrCmpLogicalW 函数)实现,仅在 Windows 平台环境中使用</para>
/// <para>基本规则如下:</para>
/// <para>将文件名中的数字作为数值来处理。</para>
/// <para>不区分大小写。</para>
/// <para>字符类型的大致优先级顺序为:特殊符号 → 数字 → 字母。</para>
/// <para>对于中文文件名,排序方式取决于系统的区域设置:拼音(默认)、笔画。</para>
/// </summary>
public class FileLogicalComparer : IComparer<string>
{
    [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
    private static extern int StrCmpLogicalW(string psz1, string psz2);
    private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

    public int Compare(string? x, string? y)
    {
        // 处理 null 值情况
        if (x == null && y == null) return 0;
        if (x == null) return -1;
        if (y == null) return 1;

        try
        {
            if (IsWindows)
            {
                // Windows 平台:使用原生 API
                return StrCmpLogicalW(x, y);
            }
            else
            {
                // 非 Windows 平台:使用 C# 实现的回退方案
                return NaturalCompareFallback(x, y);
            }
        }
        catch (Exception) // 捕获 DllNotFound、EntryPointNotFoundException 等异常
        {
            // 异常时使用默认的字符串比较器
            return string.Compare(x, y, StringComparison.Ordinal);
        }
    }

    /// <summary>
    /// C# 实现的自然排序回退方案(代码来自 AI,未测试!)
    /// </summary>
    private static int NaturalCompareFallback(string x, string y)
    {
        if (x == y) return 0;

        int i = 0, j = 0;

        while (i < x.Length && j < y.Length)
        {
            if (char.IsDigit(x[i]) && char.IsDigit(y[j]))
            {
                // 提取连续数字并进行数值比较
                string num1 = ExtractNumber(x, ref i);
                string num2 = ExtractNumber(y, ref j);

                if (long.TryParse(num1, out long n1) && long.TryParse(num2, out long n2))
                {
                    if (n1 != n2)
                        return n1.CompareTo(n2);
                }
                else
                {
                    // 解析失败时按字符串比较
                    int strCompare = string.Compare(num1, num2, StringComparison.Ordinal);
                    if (strCompare != 0)
                        return strCompare;
                }
            }
            else
            {
                // 非数字字符直接比较
                if (x[i] != y[j])
                    return x[i].CompareTo(y[j]);

                i++;
                j++;
            }
        }

        return x.Length.CompareTo(y.Length);
    }

    /// <summary>
    /// 从字符串中提取连续的数字序列
    /// </summary>
    private static string ExtractNumber(string str, ref int index)
    {
        int start = index;
        while (index < str.Length && char.IsDigit(str[index]))
        {
            index++;
        }
        return str.Substring(start, index - start);
    }
}
xoyozo 7 小时前
转载请注明出处
可能相关的内容