Orden de clasificación natural en C#

Resuelto Michael Kniskern asked hace 16 años • 19 respuestas

¿Alguien tiene un buen recurso o proporciona una muestra de ordenación natural en C# para una FileInfomatriz? Estoy implementando la IComparerinterfaz en mi tipo.

Michael Kniskern avatar Oct 30 '08 05:10 Michael Kniskern
Aceptado

Lo más fácil de hacer es simplemente P/Invocar la función incorporada en Windows y usarla como función de comparación en su IComparer:

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string psz1, string psz2);

Michael Kaplan tiene aquí algunos ejemplos de cómo funciona esta función y los cambios que se realizaron en Vista para que funcione de manera más intuitiva. El lado positivo de esta función es que tendrá el mismo comportamiento que la versión de Windows en la que se ejecuta; sin embargo, esto significa que difiere entre las versiones de Windows, por lo que debes considerar si esto es un problema para ti.

Entonces una implementación completa sería algo como:

[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
    public static extern int StrCmpLogicalW(string psz1, string psz2);
}

public sealed class NaturalStringComparer : IComparer<string>
{
    public int Compare(string a, string b)
    {
        return SafeNativeMethods.StrCmpLogicalW(a, b);
    }
}

public sealed class NaturalFileInfoNameComparer : IComparer<FileInfo>
{
    public int Compare(FileInfo a, FileInfo b)
    {
        return SafeNativeMethods.StrCmpLogicalW(a.Name, b.Name);
    }
}
Greg Beech avatar Oct 29 '2008 22:10 Greg Beech

Sólo pensé en agregar a esto (con la solución más concisa que pude encontrar):

public static IOrderedEnumerable<T> OrderByAlphaNumeric<T>(this IEnumerable<T> source, Func<T, string> selector)
{
    int max = source
        .SelectMany(i => Regex.Matches(selector(i), @"\d+").Cast<Match>().Select(m => (int?)m.Value.Length))
        .Max() ?? 0;

    return source.OrderBy(i => Regex.Replace(selector(i), @"\d+", m => m.Value.PadLeft(max, '0')));
}

Lo anterior rellena cualquier número en la cadena hasta la longitud máxima de todos los números en todas las cadenas y usa la cadena resultante para ordenar.

La conversión a ( int?) es para permitir colecciones de cadenas sin ningún número ( .Max()en un enumerable vacío arroja un InvalidOperationException).

Matthew Horsley avatar Jul 30 '2012 11:07 Matthew Horsley