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í?".

Následující faktory komplikují tyto dvě otázky:

  • Můžete zvolit řadové nebo lingvistické porovnání.
  • Můžete zvolit, jestli záleží na případu.
  • Můžete zvolit kulturně specifická porovnání.
  • Lingvistická srovnání jsou závislá na kultuře a platformě.

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

  • CurrentCulture: Porovnejte řetězce pomocí pravidel řazení citlivých na kulturu a aktuální kultury.
  • 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: Porovnávejte řetězce podle pravidel řazení citlivých na kulturu a invariantní kultury.
  • 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ů.
  • Ordinační: Porovnávejte řetězce podle ordinační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ů.

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šší ji prohledávat jak softwarem, tak i lidmi. Jiná porovnání můžou zkontrolovat, jestli jsou řetězce stejné. Tyto kontroly shody se rovnají rovnosti, ale některé rozdíly, jako třeba rozdíly ve velikosti písmen, mohou být ignorovány.

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 je výchozí ordinalní srovnávání také rozlišující malá a velká písmena.

Test rovnosti s operátory String.Equals a == se liší od porovnání řetězců pomocí metod String.CompareTo a Compare(String, String). 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 kulturním povědomím pomocí aktuální kultury. Udělejte záměr svého kódu jasný voláním metody, která explicitně specifikuje typ porovnání, které se má provést.

Můžete použít is operátor a konstantní vzor jako alternativu k == v případě, že je pravý operand konstantou.

Porovnání podle pořadí, která nerozlišují malá a velká písmena

Metoda String.Equals(String, StringComparison) umožňuje zadat StringComparison hodnotu StringComparison.OrdinalIgnoreCase pro porovnání řadových hodnot bez rozlišení velkých a malý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 formátování písmen invariantní jazykové verze při provádění porovnání nezohledňujícího velikost 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 jazykové porovnání se někdy označuje jako "pořadí řazení slov". Při provádění jazykového porovnání mohou mít některé nealfanumerické znaky Unicode přiřazeny speciální váhy. Například spojovník "-" může mít přiřazenou malou váhu, aby se vedle sebe zobrazovala slova "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) je "ss" rovno německému znaku esszet "ß" jak v kulturním prostředí "en-US", tak "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");
    }
}

Na Windowsu se před verzí .NET 5 změní pořadí řazení „cop“, „coop“ a „co-op“ při změně z lingvistického porovnání na poř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 globalizační API .NET knihovny National Language Support (NLS). V .NET 5 a novějších verzích používají rozhraní API globalizace .NET knihovny International Components for Unicode (ICU), což sjednocuje chování globalizace .NET ve všech podporovaných operačních systémech.

Porovnání pomocí konkrétních kulturních kontextů

Následující příklad ukládá CultureInfo objekty pro kultury en-US a de-DE. Porovnání se provádí pomocí objektu CultureInfo k zajištění kulturně specifického porovnání. Použitá kultura ovlivňuje lingvistické 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 kulturní kontext se obvykle používají k porovnání a řazení řetězců zadaných uživateli s jinými řetězci zadanými uživateli. 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 mohou řadit odlišně v závislosti na kultuře 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í kultury:

string[] lines =
[
    @"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 =
[
    @"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 seřazuje dva řetězce. Metoda String.CompareTo poskytuje funkci porovnání. Spusťte vzorek a sledujte pořadí. Tato operace řazení používá řazení s rozlišováním velký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 =
[
    @"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 =
[
    @"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.Hashtable, System.Collections.Generic.Dictionary<TKey,TValue> a System.Collections.Generic.List<T> mají konstruktory, které přijímají System.StringComparer parametr, pokud je typ elementů nebo klíčů string. Obecně byste měli tyto konstruktory používat, kdykoli je to možné, a zadat buď StringComparer.Ordinal nebo StringComparer.OrdinalIgnoreCase.

Viz také