Quelqu'un a-t-il une bonne ressource ou fournit un échantillon d'un tri par ordre naturel en C # pour un FileInfo
tableau? J'implémente l' IComparer
interface dans mon genre.
la source
Quelqu'un a-t-il une bonne ressource ou fournit un échantillon d'un tri par ordre naturel en C # pour un FileInfo
tableau? J'implémente l' IComparer
interface dans mon genre.
La chose la plus simple à faire est de simplement P / Invoquer la fonction intégrée de Windows et de l'utiliser comme fonction de comparaison dans votre IComparer
:
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string psz1, string psz2);
Michael Kaplan a quelques exemples du fonctionnement de cette fonction ici , et les changements qui ont été apportés à Vista pour le rendre plus intuitif. Le côté positif de cette fonction est qu'elle aura le même comportement que la version de Windows sur laquelle elle s'exécute, mais cela signifie qu'elle diffère entre les versions de Windows, vous devez donc déterminer si cela vous pose un problème.
Une implémentation complète serait donc quelque chose comme:
[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>
au lieu de l'implémenterIComparer<T>
, vous obtenez une implémentationIComparer
intégrée de l'interface (non générique) qui appelle votre méthode générique, à utiliser dans les API qui l'utilisent à la place. C'est fondamentalement gratuit de faire aussi: supprimez simplement le "I" et changezpublic int Compare(...)
enpublic override int Compare(...)
. Idem pourIEqualityComparer<T>
etEqualityComparer<T>
.Je pensais juste ajouter à cela (avec la solution la plus concise que je puisse trouver):
Ce qui précède remplit tous les nombres de la chaîne à la longueur maximale de tous les nombres de toutes les chaînes et utilise la chaîne résultante pour trier.
Le transtypage en (
int?
) est de permettre des collections de chaînes sans aucun nombre (.Max()
sur un énumérable vide lance unInvalidOperationException
).la source
.DefaultIfEmpty().Max()
au lieu de lancerint?
. Il vaut également la peine de faire unsource.ToList()
pour éviter de réénumérer l'énumérable.Aucune des implémentations existantes n'avait l'air super, j'ai donc écrit la mienne. Les résultats sont presque identiques au tri utilisé par les versions modernes de l'Explorateur Windows (Windows 7/8). Les seules différences que j'ai vues sont 1) bien que Windows utilisait (par exemple XP) des nombres de n'importe quelle longueur, il est maintenant limité à 19 chiffres - le mien est illimité, 2) Windows donne des résultats incohérents avec certains ensembles de chiffres Unicode - le mien fonctionne très bien (bien qu'il ne compare pas numériquement les chiffres des paires de substitution; pas plus que Windows), et 3) le mien ne peut pas distinguer différents types de poids de tri non primaires s'ils se produisent dans différentes sections (par exemple "e-1é" vs " é1e- "- les sections avant et après le nombre présentent des différences de poids diacritiques et de ponctuation).
La signature correspond au
Comparison<string>
délégué:Voici une classe wrapper à utiliser comme
IComparer<string>
:Exemple:
Voici un bon ensemble de noms de fichiers que j'utilise pour les tests:
la source
Solution pure C # pour linq orderby:
http://zootfroot.blogspot.com/2009/09/natural-sort-compare-with-linq-orderby.html
la source
La réponse de Matthews Horsleys est la méthode la plus rapide qui ne change pas de comportement en fonction de la version de Windows sur laquelle votre programme est exécuté. Cependant, cela peut être encore plus rapide en créant une fois l'expression régulière et en utilisant RegexOptions.Compiled. J'ai également ajouté la possibilité d'insérer un comparateur de chaînes afin que vous puissiez ignorer la casse si nécessaire, et amélioré un peu la lisibilité.
Utiliser par
Cela prend 450 ms pour trier 100 000 chaînes contre 300 ms pour la comparaison de chaînes .net par défaut - assez rapide!
la source
Ma solution:
Résultats:
la source
Vous devez être prudent - je me souviens vaguement avoir lu que StrCmpLogicalW, ou quelque chose comme ça, n'était pas strictement transitif, et j'ai observé que les méthodes de tri de .NET se coinçaient parfois dans des boucles infinies si la fonction de comparaison enfreignait cette règle.
Une comparaison transitive rapportera toujours que a <c si a <b et b <c. Il existe une fonction qui effectue une comparaison d'ordre de tri naturel qui ne répond pas toujours à ce critère, mais je ne me souviens pas si c'est StrCmpLogicalW ou autre.
la source
CultureInfo
a une propriétéCompareInfo
, et l'objet qu'il renvoie peut vous fournir desSortKey
objets. Ceux-ci, à leur tour, peuvent être comparés et garantir la transitivité.Ceci est mon code pour trier une chaîne comportant à la fois des caractères alphanumériques.
Tout d'abord, cette méthode d'extension:
Ensuite, utilisez-le simplement n'importe où dans votre code comme ceci:
Comment ça fonctionne ? En remplaçant par des zéros:
Fonctionne avec des nombres multiples:
J'espère que cela aidera.
la source
En plus de la réponse de Greg Beech (parce que je viens de chercher cela), si vous voulez utiliser ceci de Linq, vous pouvez utiliser le
OrderBy
qui prend unIComparer
. Par exemple:la source
Voici un exemple relativement simple qui n'utilise pas P / Invoke et évite toute allocation pendant l'exécution.
Il n'ignore pas les zéros non significatifs, donc
01
vient après2
.Test unitaire correspondant:
la source
Je l'ai en fait implémenté comme méthode d'extension sur le
StringComparer
afin que vous puissiez faire par exemple:StringComparer.CurrentCulture.WithNaturalSort()
ouStringComparer.OrdinalIgnoreCase.WithNaturalSort()
.Résultant
IComparer<string>
peut être utilisé dans tous les endroits commeOrderBy
,OrderByDescending
,ThenBy
,ThenByDescending
,SortedSet<string>
, etc. Et vous pouvez toujours la casse facilement tweak, la culture, etc.L'implémentation est assez triviale et devrait fonctionner assez bien même sur de grandes séquences.
Je l'ai également publié sous forme de petit package NuGet , vous pouvez donc simplement le faire:
Le code comprenant les commentaires de documentation XML et la suite de tests est disponible dans le référentiel GitHub NaturalSort.Extension .
Le code entier est le suivant (si vous ne pouvez pas encore utiliser C # 7, installez simplement le package NuGet):
la source
Voici une méthode LINQ naïve d'une ligne sans regex (empruntée à python):
la source
Dump()
. Merci de l'avoir signalé.En développant quelques-unes des réponses précédentes et en utilisant des méthodes d'extension, j'ai proposé ce qui suit qui ne contient pas les mises en garde concernant une énumération potentielle multiple énumérable, ou des problèmes de performances liés à l'utilisation de plusieurs objets regex, ou à l'appel de regex inutilement, qui étant dit, il utilise ToList (), ce qui peut annuler les avantages dans les grandes collections.
Le sélecteur prend en charge le typage générique pour permettre à n'importe quel délégué d'être affecté, les éléments de la collection source sont mutés par le sélecteur, puis convertis en chaînes avec ToString ().
la source
Inspiré de la solution de Michael Parker, voici une
IComparer
implémentation que vous pouvez ajouter à l'une des méthodes de commande linq:la source
Nous avions besoin d'un tri naturel pour traiter le texte avec le modèle suivant:
Pour une raison quelconque, lorsque j'ai regardé SO pour la première fois, je n'ai pas trouvé cet article et mis en œuvre le nôtre. Comparée à certaines des solutions présentées ici, bien que de concept similaire, elle pourrait avoir l'avantage d'être peut-être plus simple et plus facile à comprendre. Cependant, même si j'ai essayé d'examiner les goulots d'étranglement des performances, c'est toujours une mise en œuvre beaucoup plus lente que la valeur par défaut
OrderBy()
.Voici la méthode d'extension que j'implémente:
L'idée est de diviser les chaînes d'origine en blocs de chiffres et de non-chiffres (
"\d+|\D+"
). Comme il s'agit d'une tâche potentiellement coûteuse, elle n'est effectuée qu'une seule fois par entrée. Nous utilisons ensuite un comparateur d'objets comparables (désolé, je ne trouve pas de moyen plus approprié de le dire). Il compare chaque bloc à son bloc correspondant dans l'autre chaîne.J'aimerais avoir des commentaires sur la façon dont cela pourrait être amélioré et quelles sont les principales lacunes. Notez que la maintenabilité est importante pour nous à ce stade et que nous ne l'utilisons pas actuellement dans des ensembles de données extrêmement volumineux.
la source
Une version plus facile à lire / maintenir.
la source
Laissez-moi vous expliquer mon problème et comment j'ai pu le résoudre.
Problème: - Trier les fichiers en fonction de FileName à partir des objets FileInfo qui sont extraits d'un répertoire.
Solution: - J'ai sélectionné les noms de fichier de FileInfo et découpé la partie ".png" du nom de fichier. Maintenant, faites juste List.Sort (), qui trie les noms de fichiers dans l'ordre de tri naturel. Sur la base de mes tests, j'ai constaté que le fait d'avoir .png gâche l'ordre de tri. Jetez un œil au code ci-dessous
la source