¿Alguien tiene un buen recurso o proporciona una muestra de un orden natural en C # para una FileInfo
matriz? Estoy implementando la IComparer
interfaz en mi especie.
fuente
¿Alguien tiene un buen recurso o proporciona una muestra de un orden natural en C # para una FileInfo
matriz? Estoy implementando la IComparer
interfaz en mi especie.
Lo más fácil de hacer es simplemente P / Invocar la función incorporada en Windows y usarla como la 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 algunos ejemplos de cómo funciona esta función aquí , y los cambios que se hicieron para que Vista 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 debe considerar si esto es un problema para usted.
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);
}
}
Comparer<T>
lugar de implementarIComparer<T>
, obtiene una implementación integrada de laIComparer
interfaz (no genérica) que llama a su método genérico, para usar en las API que usan eso en su lugar. Básicamente, también es gratis: simplemente borre el "I" y cambiepublic int Compare(...)
apublic override int Compare(...)
. Lo mismo paraIEqualityComparer<T>
yEqualityComparer<T>
.Solo pensé en agregar a esto (con la solución más concisa que pude encontrar):
Lo anterior rellena los números en la cadena hasta la longitud máxima de todos los números en todas las cadenas y utiliza la cadena resultante para ordenar.
La conversión a (
int?
) es para permitir colecciones de cadenas sin ningún número (.Max()
en un vacío enumerable arroja unInvalidOperationException
).fuente
.DefaultIfEmpty().Max()
lugar de lanzarloint?
. También vale la pena hacer unasource.ToList()
para evitar volver a enumerar lo enumerable.Ninguna de las implementaciones existentes se veía genial, así que escribí la mía. Los resultados son casi idénticos a la clasificación utilizada por las versiones modernas de Windows Explorer (Windows 7/8). Las únicas diferencias que he visto son 1) aunque Windows solía (por ejemplo, XP) manejar números de cualquier longitud, ahora está limitado a 19 dígitos: el mío es ilimitado, 2) Windows da resultados inconsistentes con ciertos conjuntos de dígitos Unicode: el mío funciona bien (aunque no compara numéricamente dígitos de pares sustitutos; tampoco Windows), y 3) el mío no puede distinguir diferentes tipos de pesos de clasificación no primarios si ocurren en diferentes secciones (por ejemplo, "e-1é" vs " é1e- ": las secciones anteriores y posteriores al número tienen diferencias de peso diacríticas y de puntuación).
La firma coincide con el
Comparison<string>
delegado:Aquí hay una clase de contenedor para usar como
IComparer<string>
:Ejemplo:
Aquí hay un buen conjunto de nombres de archivo que uso para las pruebas:
fuente
Solución C # pura para linq orderby:
http://zootfroot.blogspot.com/2009/09/natural-sort-compare-with-linq-orderby.html
fuente
La respuesta de Matthews Horsleys es el método más rápido que no cambia el comportamiento según la versión de Windows en la que se esté ejecutando su programa. Sin embargo, puede ser aún más rápido creando la expresión regular una vez y usando RegexOptions.Compiled. También agregué la opción de insertar un comparador de cadenas para que pueda ignorar mayúsculas y minúsculas si es necesario, y mejorar un poco la legibilidad.
Usar por
Esto toma 450ms para ordenar 100,000 cadenas en comparación con 300ms para la comparación predeterminada de cadenas .net, ¡bastante rápido!
fuente
Mi solución:
Resultados:
fuente
Debe tener cuidado: recuerdo vagamente haber leído que StrCmpLogicalW, o algo así, no era estrictamente transitivo, y he observado que los métodos de ordenación de .NET a veces se atascan en bucles infinitos si la función de comparación rompe esa regla.
Una comparación transitiva siempre informará que a <c si a <by b <c. Existe una función que hace una comparación de orden de clasificación natural que no siempre cumple con ese criterio, pero no puedo recordar si es StrCmpLogicalW u otra cosa.
fuente
CultureInfo
tiene una propiedadCompareInfo
, y el objeto que devuelve puede proporcionarleSortKey
objetos. Estos, a su vez, pueden compararse y garantizar la transitividad.Este es mi código para ordenar una cadena que tiene caracteres alfabéticos y numéricos.
Primero, este método de extensión:
Luego, simplemente utilícelo en cualquier parte de su código como este:
Cómo funciona ? Al reemplazar con ceros:
Funciona con números múltiples:
Espero que eso ayude.
fuente
Agregando a la respuesta de Greg Beech (porque he estado buscando eso), si quieres usar esto de Linq, puedes usar el
OrderBy
que toma unIComparer
. P.ej:fuente
Aquí hay un ejemplo relativamente simple que no usa P / Invoke y evita cualquier asignación durante la ejecución.
No ignora los ceros a la izquierda, así que
01
viene después2
.Prueba de unidad correspondiente:
fuente
De hecho, lo he implementado como un método de extensión
StringComparer
para que puedas hacer, por ejemplo:StringComparer.CurrentCulture.WithNaturalSort()
oStringComparer.OrdinalIgnoreCase.WithNaturalSort()
.El resultante
IComparer<string>
se puede utilizar en todos los lugares comoOrderBy
,OrderByDescending
,ThenBy
,ThenByDescending
,SortedSet<string>
, etc, y que pueda mayúsculas y minúsculas todavía fácilmente pellizco, la cultura, etc.La implementación es bastante trivial y debería funcionar bastante bien incluso en secuencias grandes.
También lo publiqué como un pequeño paquete NuGet , así que puedes hacer lo siguiente:
El código que incluye comentarios de documentación XML y un conjunto de pruebas está disponible en el repositorio NaturalSort.Extension GitHub .
El código completo es este (si aún no puede usar C # 7, simplemente instale el paquete NuGet):
fuente
Aquí hay una ingenua forma de LINQ sin expresiones regulares de una línea (prestada de python):
fuente
Dump()
. Gracias por señalarlo.Ampliando un par de respuestas anteriores y haciendo uso de métodos de extensión, se me ocurrió lo siguiente que no tiene las advertencias de una posible enumeración enumerable múltiple o problemas de rendimiento relacionados con el uso de múltiples objetos regex, o llamar a regex innecesariamente, que Dicho esto, utiliza ToList (), que puede negar los beneficios en colecciones más grandes.
El selector admite la tipificación genérica para permitir que se asigne cualquier delegado, el selector muta los elementos de la colección de origen y luego los convierte en cadenas con ToString ().
fuente
Inspirada en la solución de Michael Parker, aquí hay una
IComparer
implementación que puede incluir en cualquiera de los métodos de pedido de linq:fuente
Necesitábamos un tipo natural para tratar el texto con el siguiente patrón:
Por alguna razón, cuando vi por primera vez SO, no encontré esta publicación e implementé la nuestra. En comparación con algunas de las soluciones presentadas aquí, aunque es similar en concepto, podría tener el beneficio de ser más simple y fácil de entender. Sin embargo, aunque intenté ver los cuellos de botella de rendimiento, sigue siendo una implementación mucho más lenta que la predeterminada
OrderBy()
.Aquí está el método de extensión que implemento:
La idea es dividir las cadenas originales en bloques de dígitos y no dígitos (
"\d+|\D+"
). Dado que esta es una tarea potencialmente costosa, se realiza solo una vez por entrada. Luego usamos un comparador de objetos comparables (lo siento, no puedo encontrar una manera más adecuada de decirlo). Compara cada bloque con su bloque correspondiente en la otra cadena.Me gustaría recibir comentarios sobre cómo esto podría mejorarse y cuáles son los principales defectos. Tenga en cuenta que la mantenibilidad es importante para nosotros en este momento y actualmente no la estamos utilizando en conjuntos de datos extremadamente grandes.
fuente
Una versión que es más fácil de leer / mantener.
fuente
Permítanme explicar mi problema y cómo pude resolverlo.
Problema: - Ordene los archivos basados en FileName de los objetos FileInfo que se recuperan de un Directorio.
Solución: seleccioné los nombres de archivo de FileInfo y recorté la parte ".png" del nombre del archivo. Ahora, simplemente haga List.Sort (), que clasifica los nombres de archivo en orden de clasificación natural. Según mis pruebas, descubrí que tener .png desordena el orden de clasificación. Echa un vistazo al siguiente código
fuente