Orden de clasificación natural en C#
¿Alguien tiene un buen recurso o proporciona una muestra de ordenación natural en C# para una FileInfo
matriz? Estoy implementando la IComparer
interfaz en mi tipo.
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);
}
}
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
).