it-swarm.com.de

Prüfen Sie, ob eine Zeichenfolge ein Element aus einer Liste (von Zeichenfolgen) enthält.

Für den folgenden Codeblock:

For I = 0 To listOfStrings.Count - 1
    If myString.Contains(lstOfStrings.Item(I)) Then
        Return True
    End If
Next
Return False

Die Ausgabe ist:

Fall 1:

myString: C:\Files\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: True

Fall 2:

myString: C:\Files3\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: False

Die Liste (listOfStrings) kann mehrere Elemente enthalten (mindestens 20) und muss mit Tausenden von Zeichenfolgen (wie myString) verglichen werden.

Gibt es eine bessere (effizientere) Möglichkeit, diesen Code zu schreiben?

127
user57175

Mit LINQ und der Verwendung von C # (ich kenne VB heutzutage nicht viel):

bool b = listOfStrings.Any(s=>myString.Contains(s));

oder (kürzer und effizienter, aber wohl weniger klar):

bool b = listOfStrings.Any(myString.Contains);

Wenn Sie die Gleichheit testen, lohnt sich ein Blick auf HashSet etc. Dies hilft jedoch nicht bei partiellen Übereinstimmungen, wenn Sie sie nicht in Fragmente aufteilen und eine Reihenfolge der Komplexität hinzufügen.


update: Wenn Sie wirklich "StartsWith" meinen, können Sie die Liste sortieren und in ein Array einfügen. Verwenden Sie dann Array.BinarySearch, um jedes Element zu finden. Überprüfen Sie anhand der Suche, ob es sich um eine vollständige oder teilweise Übereinstimmung handelt.

283
Marc Gravell

wenn Sie Ihre Strings konstruieren, sollte es so sein

bool inact = new string[] { "SUSPENDARE", "DIZOLVARE" }.Any(s=>stare.Contains(s));
5
Simi2525

Es gab eine Reihe von Vorschlägen aus einer früheren ähnlichen Frage " Beste Methode, um den vorhandenen String gegen eine große Liste von Vergleichbaren zu testen ".

Regex könnte für Ihre Anforderung ausreichend sein. Der Ausdruck wäre eine Verkettung aller in Frage kommenden Teilzeichenfolgen mit einem OR "|" -Operator zwischen ihnen. Natürlich müssen Sie beim Erstellen des Ausdrucks auf nicht maskierte Zeichen achten oder auf einen Fehler bei der Kompilierung aufgrund von Komplexitäts- oder Größenbeschränkungen achten.

Eine andere Möglichkeit, dies zu tun, wäre die Erstellung einer trie-Datenstruktur , um alle Kandidaten-Teilzeichenfolgen darzustellen (dies kann etwas die Wirkung des Regex-Matchers beeinträchtigen). Beim Durchlaufen der einzelnen Zeichen in der Testzeichenfolge erstellen Sie einen neuen Zeiger auf das Stammverzeichnis der Trie und verschieben vorhandene Zeiger auf das entsprechende untergeordnete Element (sofern vorhanden). Sie erhalten eine Übereinstimmung, wenn ein Zeiger ein Blatt erreicht.

5
Zach Scrivena

Ich mochte Marc's Antwort, brauchte aber die Entsprechung Enthält CaSe InSenSiTiVe.

Dies war die Lösung:

bool b = listOfStrings.Any(s => myString.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0))
3
WhoIsRich

Basierend auf Ihren Mustern wäre eine Verbesserung die Verwendung von StartsWith anstelle von Enthält. StartsWith muss nur durch jede Zeichenfolge iterieren, bis es die erste Nichtübereinstimmung findet, anstatt die Suche an jeder Zeichenposition erneut starten zu müssen, wenn eine gefunden wird.

Auf der Grundlage Ihrer Muster scheint es auch so zu sein, dass Sie möglicherweise den ersten Teil des Pfads für myString extrahieren und den Vergleich rückgängig machen können. Dabei wird nach dem Startpfad von myString in der Liste der Zeichenfolgen gesucht und nicht umgekehrt.

string[] pathComponents = myString.Split( Path.DirectorySeparatorChar );
string startPath = pathComponents[0] + Path.DirectorySeparatorChar;

return listOfStrings.Contains( startPath );

EDIT: Dies wäre mit der HashSet-Idee von @Marc Gravell noch schneller, da Sie Contains in ContainsKey ändern könnten und die Suche O(1) anstelle von O (N) wäre. Sie müssen sicherstellen, dass die Pfade genau übereinstimmen. Beachten Sie, dass dies keine generelle Lösung ist, wie @Marc Gravell, sondern auf Ihre Beispiele zugeschnitten ist.

Sorry für das C # -Beispiel. Ich habe nicht genug Kaffee gehabt, um ihn in VB zu übersetzen.

2
tvanfosson

Hast du die Geschwindigkeit getestet?

haben Sie einen Beispieldatensatz erstellt und ein Profil erstellt? Es kann nicht so schlimm sein, wie Sie denken.

Dies könnte auch etwas sein, das Sie in einen separaten Thread verwandeln und die Illusion von Geschwindigkeit vermitteln können!

1
Fortyrunner

Ich bin nicht sicher, ob es effizienter ist, aber Sie könnten an Lambda Expressions denken.

1
Mark Carpenter

Der Nachteil der Contains-Methode besteht darin, dass es nicht möglich ist, einen Vergleichstyp anzugeben, der beim Vergleichen von Zeichenfolgen häufig wichtig ist. Es ist immer kulturempfindlich und Groß-/Kleinschreibung. Ich denke, die Antwort von WhoIsRich ist wertvoll, ich möchte nur eine einfachere Alternative zeigen:

listOfStrings.Any(s => s.Equals(myString, StringComparison.OrdinalIgnoreCase))
0
Al Kepp
myList.Any(myString.Contains);
0
WIRN

Alte Frage. Aber da war VB.NET die ursprüngliche Anforderung. Verwenden Sie die gleichen Werte der akzeptierten Antwort:

listOfStrings.Any(Function(s) myString.Contains(s))
0
Luis Lavieri

Wenn die Geschwindigkeit von entscheidender Bedeutung ist, sollten Sie nach dem Aho-Corasick-Algorithmus nach Mustersätzen suchen. 

Es ist ein trie mit Fehlerlinks, dh die Komplexität ist O (n + m + k), wobei n die Länge des Eingabetextes ist, m die kumulative Länge der Muster und k die Anzahl der Übereinstimmungen ist. Sie müssen nur den Algorithmus so ändern, dass er beendet wird, nachdem die erste Übereinstimmung gefunden wurde.

0
Torsten Marek