Sdílet prostřednictvím


Porovnání řetězců v jazyce C#

Porovnáváte řetězce a odpovídáte na jednu ze dvou otázek: "Jsou tyto dva řetězce stejné?" nebo "V jakém pořadí mají být tyto řetězce umístěny při jejich řazení?".

Tyto dvě otázky jsou složité faktory, které ovlivňují porovnání řetězců:

  • Můžete zvolit řadové nebo lingvistické porovnání.
  • Můžete zvolit, jestli záleží na případu.
  • Můžete zvolit porovnání specifická pro jazykovou verzi.
  • Lingvistické porovnání jsou jazykové verze a závislé na platformě.

Pole System.StringComparison výčtu představují tyto volby:

  • CurrentCulture: Porovnejte řetězce pomocí pravidel řazení citlivých na jazykovou verzi a aktuální jazykové verze.
  • CurrentCultureIgnoreCase: Porovnání řetězců pomocí pravidel řazení citlivých na jazykovou verzi, aktuální jazykové verze a ignorování případu porovnávaných řetězců.
  • InvariantCulture: Porovnejte řetězce pomocí pravidel řazení citlivých na jazykovou verzi a invariantní jazykové verze.
  • InvariantCultureIgnoreCase: Porovnání řetězců pomocí pravidel řazení citlivých na jazykovou verzi, invariantní jazykové verze a ignorování případu porovnávaných řetězců.
  • Pořadové číslo: Porovnejte řetězce pomocí řadových (binárních) pravidel řazení.
  • OrdinalIgnoreCase: Porovnávání řetězců pomocí pravidel řazení řad (binární) a ignorování případu porovnávaných řetězců.

Poznámka:

Příklady jazyka C# v tomto článku se spouštějí v Try.NET inline code runner a playground. Vyberte tlačítko Spustit a spusťte příklad v interaktivním okně. Jakmile kód spustíte, můžete ho upravit a spustit upravený kód tak , že znovu vyberete Spustit . Upravený kód se buď spustí v interaktivním okně, nebo pokud kompilace selže, zobrazí se v interaktivním okně všechny chybové zprávy kompilátoru jazyka C#.

Při porovnávání řetězců definujete pořadí mezi nimi. Porovnání se používají k seřazení posloupnosti řetězců. Jakmile je sekvence ve známém pořadí, je jednodušší hledat software i pro lidi. Jiná porovnání můžou zkontrolovat, jestli jsou řetězce stejné. Tyto kontroly podobnosti jsou podobné rovnosti, ale některé rozdíly, jako jsou rozdíly v malých a malých případech, se můžou ignorovat.

Výchozí řadová porovnání

Ve výchozím nastavení nejběžnější operace:

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")}");

Výchozí řadové porovnání při porovnávání řetězců nebere v úvahu jazyková pravidla. Porovná binární hodnotu každého Char objektu ve dvou řetězcích. V důsledku toho se ve výchozím pořadí rozlišují malá a velká písmena.

Test rovnosti s String.Equals a == operátory se != liší od porovnání řetězců pomocí String.CompareTo metod a Compare(String, String) metod. Všechny provádějí porovnání s rozlišováním velkých a malých písmen. Zatímco testy rovnosti provádějí řadové porovnání, CompareTo a Compare metody provádějí jazykové porovnání s jazykovou verzí pomocí aktuální jazykové verze. Záměr kódu vymažte voláním přetížení, které explicitně určuje typ porovnání, který se má provést.

Porovnání řadových řad nerozlišují malá a velká písmena

Metoda String.Equals(String, StringComparison) umožňuje zadat StringComparison hodnotu StringComparison.OrdinalIgnoreCase pro porovnání řadových písmen. Existuje také statická String.Compare(String, String, StringComparison) metoda, která provádí porovnávání bez rozlišování malých a velkých písmen, pokud zadáte hodnotu argumentu StringComparison.OrdinalIgnoreCaseStringComparison . Tato porovnání jsou uvedena v následujícím kódu:

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");

Tyto metody používají konvence velikosti písmen invariantní jazykové verze při provádění porovnání bez rozlišování malých a velkých písmen.

Lingvistické porovnání

Mnoho metod porovnání řetězců (například String.StartsWith) používá jazyková pravidla pro aktuální jazykovou verzi ve výchozím nastavení k seřazení jejich vstupů. Toto lingvistické porovnání se někdy označuje jako "pořadí řazení slov". Při provádění lingvistického porovnání můžou mít některé znaky Unicode jiné než osamocené znaky přiřazené speciální váhy. Například spojovník "-" může mít přiřazenou malou váhu, aby se vedle sebe zobrazovaly "co-op" a "coop" v pořadí řazení. Některé netisknutelné řídicí znaky mohou být ignorovány. Některé znaky Unicode mohou být navíc ekvivalentní sekvenci Char instancí. Následující příklad používá frázi "Oni tancují na ulici" v němčině s "ss" (U+0073 U+0073) v jednom řetězci a "ß" (U+00DF) v jiné. Jazykově (ve Windows) se "ss" rovná německému esszetu: "ß" v jazykové verzi "en-US" i "de-DE".

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");
}

Ve Windows se před .NET 5 změní pořadí řazení "cop", "coop" a "co-op" při změně z lingvistického porovnání na řadové porovnání. Dvě německé věty také porovnávají různě pomocí různých typů porovnání. Před .NET 5 používala rozhraní API globalizace .NET knihovny národní jazykové podpory (NLS ). V rozhraních .NET 5 a novějších verzích používají rozhraní API globalizace .NET knihovny International Components for Unicode (ICU), které se sjednocují . Chování globalizace net ve všech podporovaných operačních systémech

Porovnání s využitím konkrétních jazykových verzí

Následující příklad ukládá CultureInfo objekty pro jazykové verze en-US a de-DE. Porovnání se provádí pomocí objektu CultureInfo k zajištění porovnání specifické pro jazykovou verzi. Použitá jazyková verze ovlivňuje jazykové porovnání. Následující příklad ukazuje výsledky porovnání dvou německých vět pomocí jazykové verze "en-US" a jazykové verze "de-DE":

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");
}

Porovnání citlivá na jazykovou verzi se obvykle používají k porovnání a řazení vstupních řetězců uživateli s jinými řetězci, které uživatelé zadávají. Znaky a konvence řazení těchto řetězců se mohou lišit v závislosti na národním prostředí počítače uživatele. Dokonce i řetězce, které obsahují identické znaky, se můžou řadit odlišně v závislosti na jazykové verzi aktuálního vlákna.

Lingvistické řazení a vyhledávání řetězců v polích

Následující příklady ukazují, jak řadit a hledat řetězce v poli pomocí lingvistického porovnání závislého na aktuální jazykové verzi. Použijete statické Array metody, které přebírají System.StringComparer parametr.

Následující příklad ukazuje, jak seřadit pole řetězců pomocí aktuální jazykové verze:

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}");
}

Jakmile se pole seřadí, můžete položky vyhledat pomocí binárního vyhledávání. Binární vyhledávání začíná uprostřed kolekce, aby bylo možné určit, která polovina kolekce bude obsahovat hledaný řetězec. Každé následné porovnání rozdělí zbývající část kolekce v polovině. Pole je seřazeno pomocí .StringComparer.CurrentCulture Místní funkce ShowWhere zobrazí informace o tom, kde byl řetězec nalezen. Pokud nebyl řetězec nalezen, vrácená hodnota označuje, kde by byla nalezena.

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}.");
    }
}

Řadové řazení a vyhledávání v kolekcích

Následující kód používá System.Collections.Generic.List<T> třídu kolekce k ukládání řetězců. Řetězce se seřadí pomocí List<T>.Sort metody. Tato metoda potřebuje delegáta, který porovnává a objednává dva řetězce. Metoda String.CompareTo poskytuje funkci porovnání. Spusťte ukázku a sledujte pořadí. Tato operace řazení používá řazení s rozlišováním malých a malých písmen. Statické String.Compare metody byste použili k určení různých srovnávacích pravidel.

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}");
}

Po seřazení je možné seznam řetězců prohledávat pomocí binárního vyhledávání. Následující ukázka ukazuje, jak prohledat seřazený seznam pomocí stejné funkce porovnání. Místní funkce ShowWhere ukazuje, kde je hledaný text nebo by byl:

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}.");
    }
}

Vždy se ujistěte, že pro řazení a vyhledávání používáte stejný typ porovnání. Použití různých typů porovnání pro řazení a vyhledávání vytváří neočekávané výsledky.

Třídy kolekcí, jako System.Collections.Hashtableje , System.Collections.Generic.Dictionary<TKey,TValue>a System.Collections.Generic.List<T> mají konstruktory, které přebírají System.StringComparer parametr, pokud typ elementů nebo klíčů je string. Obecně byste měli tyto konstruktory používat, kdykoli je to možné, a zadat buď StringComparer.Ordinal nebo StringComparer.OrdinalIgnoreCase.

Viz také