c# extern natural-sort

C#: Implementación de, o alternativa a, StrCmpLogicalW en shlwapi.dll



extern natural-sort (4)

Para una clasificación natural en mi aplicación, actualmente P / Invoco una función llamada StrCmpLogicalW en shlwapi.dll. Estaba pensando en intentar ejecutar mi aplicación bajo Mono, pero, por supuesto, no puedo tener estas cosas de P / Invoke (que yo sepa).

¿Es posible ver la implementación de ese método en algún lugar o hay un fragmento de código de C # bueno, limpio y eficiente que hace lo mismo?

Mi código actualmente se ve así:

[SuppressUnmanagedCodeSecurity] internal static class SafeNativeMethods { [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] public static extern int StrCmpLogicalW(string psz1, string psz2); } public class NaturalStringComparer : IComparer<string> { private readonly int modifier = 1; public NaturalStringComparer() : this(false) {} public NaturalStringComparer(bool descending) { if (descending) modifier = -1; } public int Compare(string a, string b) { return SafeNativeMethods.StrCmpLogicalW(a ?? "", b ?? "") * modifier; } }

Entonces, lo que busco es una alternativa a la clase anterior que no usa una función externa.


Acabo de implementar la comparación de cadenas naturales en C #, tal vez alguien pueda encontrarlo útil:

public class NaturalComparer : IComparer<string> { public int Compare(string x, string y) { if (x == null && y == null) return 0; if (x == null) return -1; if (y == null) return 1; int lx = x.Length, ly = y.Length; for (int mx = 0, my = 0; mx < lx && my < ly; mx++, my++) { if (char.IsDigit(x[mx]) && char.IsDigit(y[my])) { long vx = 0, vy = 0; for (; mx < lx && char.IsDigit(x[mx]); mx++) vx = vx * 10 + x[mx] - ''0''; for (; my < ly && char.IsDigit(y[my]); my++) vy = vy * 10 + y[my] - ''0''; if (vx != vy) return vx > vy ? 1 : -1; } if (mx < lx && my < ly && x[mx] != y[my]) return x[mx] > y[my] ? 1 : -1; } return lx - ly; } }


Si está ejecutando en Windows XP o más reciente, puede PInvoke a la función de shell StrCmpLogicalW:

public static int StrCmpLogical(String s1, String s2) { if (String.IsNullOrEmpty(s1) && !String.IsNullOrEmpty(s2)) return 1; //empty s1 comes after s2 else if (String.IsNullOrEmpty(s2) && !String.IsNullOrEmpty(s1)) return -1; //non-empty string comes before empty return SafeNativeMethods.StrCmpLogicalW(s1, s2); }

Y luego la clase interna insegura:

/// <summary> /// This class suppresses stack walks for unmanaged code permission. /// (System.Security.SuppressUnmanagedCodeSecurityAttribute is applied to this class.) /// This class is for methods that are safe for anyone to call. /// Callers of these methods are not required to perform a full security review to make sure that the /// usage is secure because the methods are harmless for any caller. /// </summary> [SuppressUnmanagedCodeSecurity] internal static class SafeNativeMethods { [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] internal static extern Int32 StrCmpLogicalW(string psz1, string psz2); }


Utilicé expresiones regulares para eliminar caracteres especiales. luego lanzando a int. entonces comparé enteros.

entrada:

List input = new List{ "6.04","6.01","6.03","6#04" }; Rendimiento esperado:

6.01 6.03 6.04 6#04

var output = input.OrderBy(s => s, new NaturalStringComparer()); foreach (var sort in output) { Console.WriteLine(sort); } public struct NaturalStringComparer : IComparer { public int Compare(string x, string y) { if (x == null && y == null) return 0; if (x == null) return -1; if (y == null) return 1; int lx = x.Length, ly = y.Length; int a = int.Parse(System.Text.RegularExpressions.Regex.Replace(x, @"/D+", "")); int b = int.Parse(System.Text.RegularExpressions.Regex.Replace(y, @"/D+", "")); return a.CompareTo(b); } }