Hat jemand eine gute Ressource oder stellt ein Beispiel einer Sortierung natürlicher Reihenfolge in C # für ein FileInfo
Array bereit ? Ich implementiere die IComparer
Schnittstelle in meiner Art.
quelle
Hat jemand eine gute Ressource oder stellt ein Beispiel einer Sortierung natürlicher Reihenfolge in C # für ein FileInfo
Array bereit ? Ich implementiere die IComparer
Schnittstelle in meiner Art.
Am einfachsten ist es, die in Windows integrierte Funktion aufzurufen und als Vergleichsfunktion in Ihrem IComparer
:
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string psz1, string psz2);
Michael Kaplan hat einige Beispiele dafür, wie diese Funktion hier funktioniert , und die Änderungen, die für Vista vorgenommen wurden, damit sie intuitiver funktioniert. Die positive Seite dieser Funktion ist, dass sie sich genauso verhält wie die Windows-Version, auf der sie ausgeführt wird. Dies bedeutet jedoch, dass sie sich zwischen den Windows-Versionen unterscheidet. Sie müssen also prüfen, ob dies ein Problem für Sie ist.
Eine vollständige Implementierung wäre also ungefähr so:
[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>
anstatt zu implementierenIComparer<T>
, erhalten Sie eine integrierte Implementierung derIComparer
(nicht generischen) Schnittstelle, die Ihre generische Methode aufruft, zur Verwendung in APIs, die diese stattdessen verwenden. Grundsätzlich ist es auch kostenlos: Löschen Sie einfach das "Ich" und wechseln Siepublic int Compare(...)
zupublic override int Compare(...)
. Gleiches gilt fürIEqualityComparer<T>
undEqualityComparer<T>
.Ich dachte nur, ich würde das ergänzen (mit der prägnantesten Lösung, die ich finden konnte):
Mit dem obigen Befehl werden alle Zahlen in der Zeichenfolge auf die maximale Länge aller Zahlen in allen Zeichenfolgen aufgefüllt und die resultierende Zeichenfolge zum Sortieren verwendet.
Die Umwandlung in (
int?
) soll Sammlungen von Zeichenfolgen ohne Zahlen ermöglichen (.Max()
bei einer leeren Aufzählung wirft anInvalidOperationException
).quelle
.DefaultIfEmpty().Max()
anstatt zu gießenint?
. Es lohnt sich auch, a durchzuführensource.ToList()
, um eine erneute Aufzählung der Aufzählungszeichen zu vermeiden.Keine der vorhandenen Implementierungen sah gut aus, also habe ich meine eigene geschrieben. Die Ergebnisse sind fast identisch mit der Sortierung, die von modernen Versionen von Windows Explorer (Windows 7/8) verwendet wird. Die einzigen Unterschiede, die ich gesehen habe, sind: 1) Obwohl Windows (z. B. XP) Zahlen beliebiger Länge verarbeitet hat, ist es jetzt auf 19 Stellen begrenzt - meine sind unbegrenzt. 2) Windows liefert inkonsistente Ergebnisse mit bestimmten Unicode-Stellen - meine funktioniert gut (obwohl es keine Ziffern von Ersatzpaaren numerisch vergleicht; Windows auch nicht) und 3) meine können verschiedene Arten von nicht primären Sortiergewichten nicht unterscheiden, wenn sie in verschiedenen Abschnitten vorkommen (z. B. "e-1é" vs " é1e- "- Die Abschnitte vor und nach der Zahl weisen diakritische und Interpunktionsgewichtsunterschiede auf.
Die Signatur entspricht dem
Comparison<string>
Delegierten:Hier ist eine Wrapper-Klasse zur Verwendung als
IComparer<string>
:Beispiel:
Hier ist ein guter Satz von Dateinamen, die ich zum Testen verwende:
quelle
Reine C # -Lösung für linq orderby:
http://zootfroot.blogspot.com/2009/09/natural-sort-compare-with-linq-orderby.html
quelle
Matthews Horsleys Antwort ist die schnellste Methode, die das Verhalten nicht ändert, je nachdem, auf welcher Windows-Version Ihr Programm ausgeführt wird. Es kann jedoch noch schneller sein, indem der reguläre Ausdruck einmal erstellt und RegexOptions.Compiled verwendet wird. Ich habe auch die Option hinzugefügt, einen Zeichenfolgenvergleicher einzufügen, damit Sie bei Bedarf die Groß- und Kleinschreibung ignorieren können, und die Lesbarkeit ein wenig verbessert.
Verwendung durch
Das Sortieren von 100.000 Zeichenfolgen dauert 450 ms, verglichen mit 300 ms für den standardmäßigen .net-Zeichenfolgenvergleich - ziemlich schnell!
quelle
Meine Lösung:
Ergebnisse:
quelle
Sie müssen vorsichtig sein - ich erinnere mich vage daran, dass StrCmpLogicalW oder ähnliches nicht streng transitiv war, und ich habe die Sortiermethoden von .NET beobachtet, die manchmal in Endlosschleifen stecken bleiben, wenn die Vergleichsfunktion gegen diese Regel verstößt.
Ein transitiver Vergleich zeigt immer, dass a <c ist, wenn a <b und b <c. Es gibt eine Funktion, die einen natürlichen Vergleich der Sortierreihenfolge durchführt, der dieses Kriterium nicht immer erfüllt, aber ich kann mich nicht erinnern, ob es sich um StrCmpLogicalW oder etwas anderes handelt.
quelle
CultureInfo
hat eine EigenschaftCompareInfo
und das zurückgegebene Objekt kann Sie mitSortKey
Objekten versorgen . Diese können wiederum verglichen werden und garantieren die Transitivität.Dies ist mein Code zum Sortieren einer Zeichenfolge mit alphanumerischen Zeichen.
Zunächst diese Erweiterungsmethode:
Verwenden Sie es dann einfach an einer beliebigen Stelle in Ihrem Code wie folgt:
Wie funktioniert es ? Durch Ersetzen durch Nullen:
Funktioniert mit mehreren Zahlen:
Hoffe das wird helfen.
quelle
Zusätzlich zu Greg Beech Antwort (weil ich gerade habe für die Suche), wenn Sie diese von Linq verwenden möchten , können Sie das verwenden ,
OrderBy
die eine nimmtIComparer
. Z.B:quelle
Hier ist ein relativ einfaches Beispiel, das P / Invoke nicht verwendet und jegliche Zuordnung während der Ausführung vermeidet.
Führende Nullen werden nicht ignoriert, daher
01
folgt sie2
.Entsprechender Unit-Test:
quelle
Ich habe es tatsächlich als Erweiterungsmethode auf dem implementiert,
StringComparer
damit Sie zum Beispiel Folgendes tun können:StringComparer.CurrentCulture.WithNaturalSort()
oderStringComparer.OrdinalIgnoreCase.WithNaturalSort()
.Das resultierende
IComparer<string>
kann überall dort eingesetzt werden , wieOrderBy
,OrderByDescending
,ThenBy
,ThenByDescending
,SortedSet<string>
, etc. Und Sie können immer noch leicht zwicken Groß- und Kleinschreibung, Kultur usw.Die Implementierung ist ziemlich trivial und sollte auch bei großen Sequenzen recht gut funktionieren.
Ich habe es auch als winziges NuGet-Paket veröffentlicht , sodass Sie einfach Folgendes tun können:
Der Code mit Kommentaren zur XML-Dokumentation und einer Reihe von Tests ist im GitHub-Repository von NaturalSort.Extension verfügbar .
Der gesamte Code lautet wie folgt (wenn Sie C # 7 noch nicht verwenden können, installieren Sie einfach das NuGet-Paket):
quelle
Hier ist ein naiver einzeiliger LINQ-Weg ohne Regex (aus Python entlehnt):
quelle
Dump()
. Vielen Dank für den Hinweis.Ich habe einige der vorherigen Antworten erweitert und Erweiterungsmethoden verwendet. Dabei habe ich Folgendes gefunden, das nicht die Vorbehalte einer potenziellen Aufzählung mehrerer Aufzählungen oder Leistungsprobleme aufweist, die mit der Verwendung mehrerer Regex-Objekte oder dem unnötigen Aufrufen von Regex verbunden sind Allerdings wird ToList () verwendet, wodurch die Vorteile in größeren Sammlungen zunichte gemacht werden können.
Der Selektor unterstützt die generische Typisierung, damit jeder Delegat zugewiesen werden kann. Die Elemente in der Quellensammlung werden vom Selektor mutiert und dann mit ToString () in Zeichenfolgen konvertiert.
quelle
Inspiriert von der Lösung von Michael Parker, finden Sie hier eine
IComparer
Implementierung, die Sie in eine der Linq-Bestellmethoden einbinden können:quelle
Wir brauchten eine natürliche Sorte, um mit Text mit folgendem Muster umgehen zu können:
Aus irgendeinem Grund habe ich diesen Beitrag nicht gefunden und unseren eigenen implementiert, als ich SO zum ersten Mal angesehen habe. Im Vergleich zu einigen der hier vorgestellten Lösungen ist das Konzept zwar ähnlich, es könnte jedoch den Vorteil haben, dass es möglicherweise einfacher und verständlicher ist. Obwohl ich versucht habe, Leistungsengpässe zu untersuchen, ist die Implementierung immer noch viel langsamer als die Standardimplementierung
OrderBy()
.Hier ist die Erweiterungsmethode, die ich implementiere:
Die Idee ist, die ursprünglichen Zeichenfolgen in Ziffernblöcke und Nicht-Ziffernblöcke (
"\d+|\D+"
) aufzuteilen . Da dies eine möglicherweise teure Aufgabe ist, wird sie nur einmal pro Eintrag ausgeführt. Wir verwenden dann einen Vergleich vergleichbarer Objekte (Entschuldigung, ich kann keinen angemesseneren Weg finden, dies auszudrücken). Es vergleicht jeden Block mit seinem entsprechenden Block in der anderen Zeichenfolge.Ich hätte gerne Feedback, wie dies verbessert werden könnte und was die Hauptmängel sind. Beachten Sie, dass die Wartbarkeit an dieser Stelle für uns wichtig ist und wir sie derzeit nicht in extrem großen Datenmengen verwenden.
quelle
Eine Version, die einfacher zu lesen / zu warten ist.
quelle
Lassen Sie mich mein Problem erklären und wie ich es lösen konnte.
Problem: - Sortieren Sie Dateien basierend auf FileName aus FileInfo-Objekten, die aus einem Verzeichnis abgerufen werden.
Lösung: - Ich habe die Dateinamen aus FileInfo ausgewählt und den Teil ".png" des Dateinamens gekürzt. Führen Sie jetzt einfach List.Sort () aus, das die Dateinamen in natürlicher Sortierreihenfolge sortiert. Aufgrund meiner Tests stellte ich fest, dass .png die Sortierreihenfolge durcheinander bringt. Schauen Sie sich den folgenden Code an
quelle