Сравнение строк в C#

Вы сравниваете строки, чтобы ответить на один из двух вопросов: "Равны ли эти две строки?" или "В каком порядке эти строки должны быть размещены при их сортировке?"

Однако эту задачу осложняют факторы, влияющие на сравнение строк:

  • Вы можете выбрать порядковое или лингвистическое сравнение.
  • Вы можете указать, учитывается ли регистр.
  • Вы можете выбрать сравнения для конкретного языка и региональных параметров.
  • Лингвистические сравнения зависят от языка и региональных параметров, а также от используемой платформы.

Поля System.StringComparison перечисления представляют следующие варианты:

  • CurrentCulture: сравнение строк с использованием правил сортировки с учетом языка и региональных параметров и текущего языка и региональных параметров.
  • CurrentCultureIgnoreCase: сравнивайте строки с использованием правил сортировки с учетом языка и региональных параметров, текущего языка и региональных параметров и игнорируя регистр сравниваемых строк.
  • InvariantCulture: сравнение строк с использованием правил сортировки с учетом языка и региональных параметров и инвариантного языка и региональных параметров.
  • InvariantCultureIgnoreCase: сравнивайте строки с использованием правил сортировки с учетом языка и региональных параметров, инвариантного языка и региональных параметров и игнорируя регистр сравниваемых строк.
  • Порядковый номер. Сравнение строк с помощью правил сортировки порядковых (двоичных).
  • OrdinalIgnoreCase: сравнивайте строки с использованием правил сортировки порядковых (двоичных) и игнорируя регистр сравниваемых строк.

Примечание.

Примеры C# в этой статье выполняются во встроенном средстве выполнения кода и на площадке Try.NET. Нажмите на кнопку Выполнить, чтобы выполнить пример в интерактивном окне. После выполнения кода вы можете изменить его и выполнить измененный код, снова нажав на кнопку Выполнить. Либо в интерактивном окне выполняется измененный код, либо, если компиляция завершается с ошибкой, в интерактивном окне отображаются все сообщения об ошибках компилятора C#.

При сравнении строк вы определяете их порядок. Сравнения используются для сортировки последовательности строк. После того как последовательность находится в известном порядке, проще искать программное обеспечение и для людей. Другие сравнения могут проверка, если строки одинаковы. Такие же проверка похожи на равенство, но некоторые различия, такие как различия в случае, могут игнорироваться.

Порядковые сравнения по умолчанию

Ниже представлены самые распространенные операции по умолчанию:

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

При порядковых сравнениях строк по умолчанию лингвистические правила не учитываются. В них сравнивается двоичное значение каждого объекта Char в двух строках. Таким образом, в порядковом сравнении по умолчанию также учитывается регистр.

Проверка на равенство с использованием String.Equals, а также операторов == и != отличается от сравнения строк с использованием методов String.CompareTo и Compare(String, String). Все они выполняют сравнение с учетом регистра. Однако, хотя тесты на равенство выполняют порядковое сравнение, CompareTo методы Compare и методы выполняют лингвистическое сравнение с учетом языка и региональных параметров с использованием текущего языка и региональных параметров. Очистите намерение кода, вызвав перегрузку, которая явно указывает тип сравнения для выполнения.

Порядковые сравнения без учета регистра

Этот String.Equals(String, StringComparison) метод позволяет указать StringComparison значение StringComparison.OrdinalIgnoreCase для нечувствительного порядкового сравнения регистра. Существует также статический String.Compare(String, String, StringComparison) метод, который выполняет нечувствительное порядковое сравнение регистра, если указать значение StringComparison.OrdinalIgnoreCase аргумента StringComparison . Эти сравнения показаны в следующем коде:

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

Эти методы используют соглашения о регистре инвариантного языка и региональных параметров при выполнении нечувствительного порядкового сравнения регистра.

Лингвистические сравнения

Многие методы сравнения строк (например String.StartsWith) используют лингвистические правила для текущего языка и региональных параметров по умолчанию для упорядочивания входных данных. Это лингвистическое сравнение иногда называется "порядком сортировки слов". При выполнении лингвистического сравнения некоторые неэлементные символы Юникода могут иметь специальные весовые значения. Например, дефис "-" может иметь небольшой вес, назначенный ему, чтобы "co-op" и "coop" отображались рядом друг с другом в порядке сортировки. Некоторые непечатные символы элемента управления могут игнорироваться. Кроме того, некоторые символы Юникода могут быть эквивалентны последовательности экземпляров Char . В следующем примере используется фраза "Они танцуют на улице". В немецком языке с "ss" (U+0073 U+0073) в одной строке и "ß" (U+00DF) в другом. Лингвистически (в Windows) буквы "ss" равнозначны немецкому символу эсцет "ß" в языках "en-US" и "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");
}

В Windows до .NET 5 порядок сортировки "cop", "coop" и "co-op" изменяется при изменении лингвистического сравнения на порядковое сравнение. Два предложения на немецком языке также сравниваются по-разному при использовании разных типов сравнения. До .NET 5 API глобализации .NET использовали библиотеки национальной поддержки языка (NLS ). В .NET 5 и более поздних версиях API глобализации .NET используют международные компоненты для библиотек Юникода (ICU ), которые унифицируют. Поведение глобализации NET во всех поддерживаемых операционных системах.

Сравнения с использованием определенных языков и региональных параметров

В следующем примере хранятся CultureInfo объекты для региональных параметров en-US и de-DE. Сравнения выполняются с использованием объекта CultureInfo, чтобы учесть язык и региональные параметры. Используемые значения языка и региональных параметров влияют на операции лингвистического сравнения. В следующем примере показаны результаты сравнения двух предложений на немецком с использованием языка и региональных параметров "en US" и "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");
}

Сравнения с учетом языка и региональных параметров обычно используются для сравнения и сортировки строк, вводимых пользователями. Символы и правила сортировки этих строк могут различаться в зависимости от языкового стандарта компьютера пользователя. Даже строки, содержащие идентичные символы, могут быть отсортированы по-разному, в зависимости от языка и региональных параметров текущего потока.

Лингвистическая сортировка и поиск строк в массивах

Приведенные ниже примеры показывают, как сортировать и искать строки в массиве с помощью лингвистического сравнения, зависящего от текущих значений языка и региональных параметров. Используйте статические методы Array, которые принимают параметр System.StringComparer.

В следующем примере показано, как сортировать массив строк с помощью текущего языка и региональных параметров:

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

После сортировки массива можно выполнить поиск записей с помощью двоичного поиска. Двоичный поиск начинается с середины коллекции, чтобы определить, какая половина коллекции содержит искомую строку. Каждое последующее сравнение делит оставшуюся часть коллекции пополам. Массив сортируется с использованием StringComparer.CurrentCulture. Локальная функция ShowWhere отображает сведения о том, где была найдена строка. Если строка не найдена, возвращаемое значение указывает, где бы оно находилось, если было бы найдено.

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

Порядковая сортировка и поиск в коллекциях

Следующий код использует класс коллекции System.Collections.Generic.List<T> для хранения строк. Строки сортируются с помощью метода List<T>.Sort. Этому методу нужен делегат, который сравнивает и упорядочивает две строки. Метод String.CompareTo предоставляет эту функцию сравнения. Запустите пример и следите за порядком. Эта операция сортировки использует порядковую сортировку с учетом регистра. Можно использовать статические методы String.Compare, чтобы указать разные правила сравнения.

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

После сортировки по списку строк можно осуществлять двоичный поиск. В приведенном ниже примере показан поиск в отсортированном списке с использованием той же функции сравнения. Локальная функция ShowWhere показывает, где находится или находился бы искомый текст:

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

Всегда используйте один и тот же тип сравнения для сортировки и поиска. Использование разных типов сравнения приводит к неожиданным результатам.

Классы коллекций, такие как System.Collections.Hashtable, System.Collections.Generic.Dictionary<TKey,TValue>, и System.Collections.Generic.List<T>, имеют конструкторы, принимающие параметр System.StringComparer, если типом элементов или ключей является string. В целом, по возможности следует использовать эти конструкторы и задавать либо StringComparer.Ordinal, либо StringComparer.OrdinalIgnoreCase.

См. также