Vergleichen von Zeichenfolgen in C#
Durch den Vergleich zweier Zeichenfolgen können Sie entweder feststellen, ob diese gleich sind oder in welche Reihenfolge sie beim Sortieren gebracht werden sollen.
Beide Vorgänge werden durch folgende Faktoren erschwert:
- Sie können einen Ordinalvergleich oder einen linguistischen Vergleich durchführen.
- Sie können festlegen, ob die Groß/-Kleinschreibung berücksichtigt werden soll.
- Sie können kulturspezifische Vergleiche durchführen.
- Linguistische Vergleiche sind kultur- und plattformabhängig.
Die System.StringComparison-Enumerationsfelder stellen die folgenden Auswahlmöglichkeiten dar:
- CurrentCulture: Vergleichen Sie Zeichenfolgen mit kulturabhängigen Sortierregeln und der aktuellen Kultur.
- CurrentCultureIgnoreCase: Vergleichen Sie Zeichenfolgen mithilfe von kulturabhängigen Sortierregeln und der aktuellen Kultur, und ignorieren Sie die Groß-/Kleinschreibung der zu vergleichenden Zeichenfolgen.
- InvariantCulture: Vergleichen Sie Zeichenfolgen mithilfe von kulturabhängigen Sortierregeln und der invarianten Kultur.
- InvariantCultureIgnoreCase: Vergleichen Sie Zeichenfolgen mithilfe von kulturabhängigen Sortierregeln, der invarianten Kultur, und ignorieren Sie die Groß-/Kleinschreibung der zu vergleichenden Zeichenfolgen.
- Ordinal-: Vergleichen Sie Zeichenfolgen mithilfe von ordinalen (binären) Sortierregeln.
- OrdinalIgnoreCase: Vergleichen Sie Zeichenfolgen mithilfe von ordinalen (binären) Sortierregeln, und ignorieren Sie die Groß-/Kleinschreibung der zu vergleichenden Zeichenfolgen.
Hinweis
Die C#-Beispiele in diesem Artikel werden in der Inlinecodeausführung und dem Playground von Try.NET ausgeführt. Klicken Sie auf die Schaltfläche Ausführen, um ein Beispiel in einem interaktiven Fenster auszuführen. Nachdem Sie den Code ausgeführt haben, können Sie ihn ändern und den geänderten Code durch erneutes Anklicken der Schaltfläche Ausführen ausführen. Der geänderte Code wird entweder im interaktiven Fenster ausgeführt, oder das interaktive Fenster zeigt alle C#-Compilerfehlermeldungen an, wenn die Kompilierung fehlschlägt.
Wenn Sie Zeichenfolgen vergleichen, definieren Sie für diese eine Reihenfolge. Bei einigen Vergleichen wird eine Zeichenfolgensequenz sortiert. Sobald sich die Sequenz in einer bekannten Reihenfolge befindet, kann sie sowohl von Software als auch von Personen einfacher durchsucht werden. Bei anderen Vergleichen kann überprüft werden, ob Zeichenfolgen identisch sind. Identitätsprüfungen sind mit Gleichheitsprüfungen zu vergleichen, doch einige Unterschiede wie die Groß-/Kleinschreibung werden eventuell ignoriert.
Standardordinalvergleich
Standardmäßig gilt für die gängigsten Vorgänge Folgendes:
- String.Equals
- String.Equality und String.Inequality, d. h. die Gleichheitsoperatoren
==
und!=
, führen jeweils einen ordinalen Vergleich durch, bei dem Groß- und Kleinschreibung beachtet wird. String.Equals verfügt über eine Überladung, bei der ein StringComparison-Argument bereitgestellt werden kann, um die Sortierregeln zu ändern. Dies wird im folgenden Beispiel veranschaulicht:
string root = @"C:\users";
string root2 = @"C:\Users";
bool result = root.Equals(root2);
Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");
result = root.Equals(root2, StringComparison.Ordinal);
Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");
Console.WriteLine($"Using == says that <{root}> and <{root2}> are {(root == root2 ? "equal" : "not equal")}");
Bei dem Standardordinalvergleich werden keine linguistischen Regeln berücksichtigt, wenn Zeichenfolgen verglichen werden. In ihm wird der binäre Wert jedes Char-Objekts in zwei Zeichenfolgen verglichen. Dies bedingt, dass im Standardordinalvergleich ebenfalls die Groß-/Kleinschreibung beachtet wird.
Der Test auf Gleichheit mit String.Equals und den Operatoren ==
und !=
unterscheidet sich von einem Zeichenfolgenvergleich mit den Methoden String.CompareTo und Compare(String, String). Sie alle führen einen Vergleich unter Beachtung der Groß- und Kleinschreibung durch. Während die Gleichheitstests jedoch einen Ordinalvergleich durchführen, führen die CompareTo
- und Compare
-Methode einen kulturbewussten linguistischen Vergleich unter Verwendung der aktuellen Kultur durch. Machen Sie den Zweck des Codes deutlich, indem Sie eine Überladung aufrufen, die explizit den auszuführenden Vergleichstyp angibt.
Ordinalvergleich ohne Beachtung der Groß- und Kleinschreibung
Mit der String.Equals(String, StringComparison)-Methode können Sie einen StringComparison-Wert von StringComparison.OrdinalIgnoreCase für einen Ordinalvergleich ohne Berücksichtigung der Groß-/Kleinschreibung angeben. Es gibt auch eine statische String.Compare(String, String, StringComparison)-Methode, die einen ordinalen Vergleich durchführt, bei dem die Groß- und Kleinschreibung beachtet wird, wenn Sie einen StringComparison.OrdinalIgnoreCase-Wert für das StringComparison-Argument angeben. Diese Vergleiche werden im folgenden Code angezeigt:
string root = @"C:\users";
string root2 = @"C:\Users";
bool result = root.Equals(root2, StringComparison.OrdinalIgnoreCase);
bool areEqual = String.Equals(root, root2, StringComparison.OrdinalIgnoreCase);
int comparison = String.Compare(root, root2, comparisonType: StringComparison.OrdinalIgnoreCase);
Console.WriteLine($"Ordinal ignore case: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");
Console.WriteLine($"Ordinal static ignore case: <{root}> and <{root2}> are {(areEqual ? "equal." : "not equal.")}");
if (comparison < 0)
Console.WriteLine($"<{root}> is less than <{root2}>");
else if (comparison > 0)
Console.WriteLine($"<{root}> is greater than <{root2}>");
else
Console.WriteLine($"<{root}> and <{root2}> are equivalent in order");
Diese Methoden verwenden die Groß-/Kleinschreibungskonventionen der invarianten Kultur für einen ordinalen Vergleich, bei dem Groß- und Kleinschreibung beachtet wird.
Linguistische Vergleiche
Viele Zeichenfolgenvergleichsmethoden (z. B. String.StartsWith) verwenden standardmäßig linguistische Regeln für die aktuelle Kultur , um ihre Eingaben zu sortieren. Dieser linguistische Vergleich wird auch als "Wortsortierreihenfolge" bezeichnet. Beim Ausführen eines linguistischen Vergleichs werden möglicherweise einigen nicht alphanumerischen Unicode-Zeichen bestimmte Gewichtungen zugewiesen. Beispielsweise wird dem Bindestrich („-“) ggf. eine geringe Gewichtung zugeordnet, sodass „coop“ und „co-op“ in einer Sortierreihenfolge nebeneinander angezeigt werden. Einige nicht druckbare Steuerzeichen werden möglicherweise ignoriert. Darüber hinaus können einige Unicode-Zeichen einer Sequenz von Char-Instanzen entsprechen. Im folgenden Beispiel werden die deutschen Sätze „Sie tanzen auf der Straße.“ und „Sie tanzen auf der Strasse.“ mit „ss“ (U+0073 U+0073) in einer Zeichenfolge und „ß“ (U+00DF) in einer anderen verwendet. Unter Windows entspricht in den Kulturen „en-US“ und „de-DE“ die Zeichenfolge „ss“ linguistisch dem scharfen S/Eszett („ß“).
string first = "Sie tanzen auf der Straße.";
string second = "Sie tanzen auf der Strasse.";
Console.WriteLine($"First sentence is <{first}>");
Console.WriteLine($"Second sentence is <{second}>");
bool equal = String.Equals(first, second, StringComparison.InvariantCulture);
Console.WriteLine($"The two strings {(equal == true ? "are" : "are not")} equal.");
showComparison(first, second);
string word = "coop";
string words = "co-op";
string other = "cop";
showComparison(word, words);
showComparison(word, other);
showComparison(words, other);
void showComparison(string one, string two)
{
int compareLinguistic = String.Compare(one, two, StringComparison.InvariantCulture);
int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal);
if (compareLinguistic < 0)
Console.WriteLine($"<{one}> is less than <{two}> using invariant culture");
else if (compareLinguistic > 0)
Console.WriteLine($"<{one}> is greater than <{two}> using invariant culture");
else
Console.WriteLine($"<{one}> and <{two}> are equivalent in order using invariant culture");
if (compareOrdinal < 0)
Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
else if (compareOrdinal > 0)
Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
else
Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
}
Unter Windows vor .NET 5 ändert sich die Sortierreihenfolge von „cop“, „coop“ und „co-op“, wenn Sie anstelle eines Ordinalvergleichs einen linguistischen Vergleich verwenden. Die unterschiedlichen Vergleichsarten führen auch bei den beiden deutschen Beispielsätzen zu unterschiedlichen Ergebnissen. Vor .NET 5 verwendeten die .NET-Globalisierungs-APIs National Language Support-Bibliotheken (NLS). In .NET 5 und höheren Versionen verwenden die .NET-Globalisierungs-APIs International Components for Unicode-Bibliotheken (ICU), die das Globalisierungsverhalten von NET für alle unterstützten Betriebssysteme vereinheitlichen.
Vergleiche unter Berücksichtigung bestimmter Kulturen
Im folgenden Beispiel werden CultureInfo-Objekte für die Kulturen „en-US“ und „de-DE“ gespeichert. Damit tatsächlich ein kulturabhängiger Vergleich verwendet wird, werden die Vergleiche mit einem CultureInfo-Objekt ausgeführt. Die verwendete Kultur wirkt sich auf linguistische Vergleiche aus. Im folgenden Beispiel werden die beiden deutsche Sätze unter Berücksichtigung der Kulturen „en-US“ und „de-DE“ verglichen und die Ergebnisse angezeigt:
string first = "Sie tanzen auf der Straße.";
string second = "Sie tanzen auf der Strasse.";
Console.WriteLine($"First sentence is <{first}>");
Console.WriteLine($"Second sentence is <{second}>");
var en = new System.Globalization.CultureInfo("en-US");
// For culture-sensitive comparisons, use the String.Compare
// overload that takes a StringComparison value.
int i = String.Compare(first, second, en, System.Globalization.CompareOptions.None);
Console.WriteLine($"Comparing in {en.Name} returns {i}.");
var de = new System.Globalization.CultureInfo("de-DE");
i = String.Compare(first, second, de, System.Globalization.CompareOptions.None);
Console.WriteLine($"Comparing in {de.Name} returns {i}.");
bool b = String.Equals(first, second, StringComparison.CurrentCulture);
Console.WriteLine($"The two strings {(b ? "are" : "are not")} equal.");
string word = "coop";
string words = "co-op";
string other = "cop";
showComparison(word, words, en);
showComparison(word, other, en);
showComparison(words, other, en);
void showComparison(string one, string two, System.Globalization.CultureInfo culture)
{
int compareLinguistic = String.Compare(one, two, en, System.Globalization.CompareOptions.None);
int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal);
if (compareLinguistic < 0)
Console.WriteLine($"<{one}> is less than <{two}> using en-US culture");
else if (compareLinguistic > 0)
Console.WriteLine($"<{one}> is greater than <{two}> using en-US culture");
else
Console.WriteLine($"<{one}> and <{two}> are equivalent in order using en-US culture");
if (compareOrdinal < 0)
Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
else if (compareOrdinal > 0)
Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
else
Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
}
Mit kulturabhängigen Vergleichen werden üblicherweise Zeichenfolgeneingaben unterschiedlicher Benutzer verglichen und sortiert. Die Zeichen und Sortierungskonventionen dieser Zeichenfolgen können allerdings je nach Gebietsschema des Benutzercomputers variieren. Sogar Zeichenfolgen, die identische Zeichen enthalten, sortieren je nach Kultur des aktuellen Threads möglicherweise unterschiedlich.
Linguistisches Sortieren und Suchen nach Zeichenfolgen in Arrays
In den folgenden Beispielen wird gezeigt, wie Sie mit einem linguistischen Vergleich, der abhängig von der aktuellen Kultur ist, Zeichenfolgen in einem Array sortieren und nach diesen suchen. Dabei werden die statischen Array-Methoden verwendet, die einen System.StringComparer-Parameter akzeptieren.
Im folgenden Beispiel wird demonstriert, wie Sie Zeichenfolgen in einem Array unter Berücksichtigung der aktuellen Kultur sortieren:
string[] lines = new string[]
{
@"c:\public\textfile.txt",
@"c:\public\textFile.TXT",
@"c:\public\Text.txt",
@"c:\public\testfile2.txt"
};
Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
Console.WriteLine($" {s}");
}
Console.WriteLine("\n\rSorted order:");
// Specify Ordinal to demonstrate the different behavior.
Array.Sort(lines, StringComparer.CurrentCulture);
foreach (string s in lines)
{
Console.WriteLine($" {s}");
}
Nach der Sortierung des Arrays können Sie mit einer Binärsuche nach Einträgen suchen. Bei einer Binärsuche wird als Startposition die Mitte der Collection gewählt, um zu bestimmen, welche Hälfte der Collection die gesuchte Zeichenfolge enthält. Jeder nachfolgende Vergleich unterteilt den verbleibenden Teil der Collection in zwei weitere Teile. Das Array wird mit der StringComparer.CurrentCulture-Eigenschaft sortiert. Die lokale Funktion ShowWhere
gibt Informationen zur Position aus, an der die Zeichenfolge gefunden wurde. Wenn die Zeichenfolge nicht gefunden wurde, gibt der zurückgegebene Wert an, an welcher Stelle sich die Zeichenfolge befände, wenn diese vorhanden wäre.
string[] lines = new string[]
{
@"c:\public\textfile.txt",
@"c:\public\textFile.TXT",
@"c:\public\Text.txt",
@"c:\public\testfile2.txt"
};
Array.Sort(lines, StringComparer.CurrentCulture);
string searchString = @"c:\public\TEXTFILE.TXT";
Console.WriteLine($"Binary search for <{searchString}>");
int result = Array.BinarySearch(lines, searchString, StringComparer.CurrentCulture);
ShowWhere<string>(lines, result);
Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}");
void ShowWhere<T>(T[] array, int index)
{
if (index < 0)
{
index = ~index;
Console.Write("Not found. Sorts between: ");
if (index == 0)
Console.Write("beginning of sequence and ");
else
Console.Write($"{array[index - 1]} and ");
if (index == array.Length)
Console.WriteLine("end of sequence.");
else
Console.WriteLine($"{array[index]}.");
}
else
{
Console.WriteLine($"Found at index {index}.");
}
}
Ordinales Sortieren und Suchen in Collections
Im folgenden Codebeispiel wird die System.Collections.Generic.List<T>-Collectionklasse zum Speichern von Zeichenfolgen verwendet. Die Zeichenfolgen werden mit der List<T>.Sort-Methode sortiert. Diese Methode benötigt einen Delegaten, der zwei Zeichenfolgen vergleicht und sortiert. Die String.CompareTo-Methode stellt diese Vergleichsfunktion bereit. Führen Sie das Beispiel aus, und beachten Sie die Reihenfolge. Bei diesem Sortiervorgang werden Zeichenfolgen ordinal unter Berücksichtigung der Groß-/Kleinschreibung sortiert. Mit den statischen String.Compare-Methoden können Sie unterschiedliche Vergleichsregeln angeben.
List<string> lines = new List<string>
{
@"c:\public\textfile.txt",
@"c:\public\textFile.TXT",
@"c:\public\Text.txt",
@"c:\public\testfile2.txt"
};
Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
Console.WriteLine($" {s}");
}
Console.WriteLine("\n\rSorted order:");
lines.Sort((left, right) => left.CompareTo(right));
foreach (string s in lines)
{
Console.WriteLine($" {s}");
}
Nach der Sortierung kann die Liste der Zeichenfolgen mit einer Binärsuche durchsucht werden. Im folgenden Beispiel wird gezeigt, wie Sie die sortierte Liste mit derselben Vergleichsfunktion durchsuchen. Die lokale Funktion ShowWhere
zeigt an, wo sich der gesuchte Text befindet oder befände, wenn er vorhanden wäre:
List<string> lines = new List<string>
{
@"c:\public\textfile.txt",
@"c:\public\textFile.TXT",
@"c:\public\Text.txt",
@"c:\public\testfile2.txt"
};
lines.Sort((left, right) => left.CompareTo(right));
string searchString = @"c:\public\TEXTFILE.TXT";
Console.WriteLine($"Binary search for <{searchString}>");
int result = lines.BinarySearch(searchString);
ShowWhere<string>(lines, result);
Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}");
void ShowWhere<T>(IList<T> collection, int index)
{
if (index < 0)
{
index = ~index;
Console.Write("Not found. Sorts between: ");
if (index == 0)
Console.Write("beginning of sequence and ");
else
Console.Write($"{collection[index - 1]} and ");
if (index == collection.Count)
Console.WriteLine("end of sequence.");
else
Console.WriteLine($"{collection[index]}.");
}
else
{
Console.WriteLine($"Found at index {index}.");
}
}
Achten Sie darauf, zum Sortieren und Suchen von Zeichenfolgen immer dieselbe Vergleichsart zu verwenden. Andernfalls werden unerwartete Ergebnisse angezeigt.
Auflistungsklassen wie System.Collections.Hashtable, System.Collections.Generic.Dictionary<TKey,TValue> und System.Collections.Generic.List<T> verfügen über Konstruktoren, die einen System.StringComparer-Parameter übernehmen, wenn der Typ des Elements oder der Schlüssel string
ist. Im Allgemeinen sollten Sie nach Möglichkeit diese Konstruktoren verwenden und entweder StringComparer.Ordinal oder StringComparer.OrdinalIgnoreCase angeben.