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

Tip

Эта статья является частью раздела "Основы" для разработчиков , которые уже знают хотя бы один язык программирования и учат C#. Если вы не знакомы с программированием, сначала начните с учебных пособий по началу работы.

Вы пришли из другого языка? C# == в строках сравнивает значения, а не ссылки, например Java equals или JavaScript===. Ключевое различие заключается в том, что C# позволяет выбирать между порядковым (двоичным) и сравнением с учетом языковых и региональных параметров с помощью значения StringComparison.

Вы сравниваете строки, чтобы ответить на один из двух вопросов: равны ли эти две строки? Или в каком порядке эти строки сортируются? C# позволяет управлять двумя независимыми факторами при ответе на любой вопрос:

  • Чувствительность к регистру — считаются ли "Hello" и "hello" равными.
  • Тип сравненияпорядковое (сравнивается двоичное значение каждого символа) или с учетом языковых и региональных параметров (применяются лингвистические правила определенной культуры).

Перечисление StringComparison объединяет эти факторы в одно значение, которое передается в методы сравнения.

Сравнить на равенство

Метод String.Equals и оператор == по умолчанию выполняют сравнение с учетом регистра и по порядковому значению. Порядковое сравнение проверяет двоичное значение каждого символа, поэтому оно быстро и дает одинаковый результат на каждом компьютере. Вы можете явно указать свои намерения, вызвав перегрузку, которая принимает значение StringComparison:

string root = @"C:\users";
string root2 = @"C:\Users";

// Equals and == both perform a case-sensitive, ordinal comparison.
Console.WriteLine(root.Equals(root2));
// => False
Console.WriteLine(root == root2);
// => False

// Pass a StringComparison value to state the intent explicitly.
Console.WriteLine(root.Equals(root2, StringComparison.Ordinal));
// => False

Чтобы игнорировать регистр при сохранении порядковой семантики, передайте StringComparison.OrdinalIgnoreCase:

string root = @"C:\users";
string root2 = @"C:\Users";

// OrdinalIgnoreCase compares the binary values but ignores case.
bool equalIgnoringCase = string.Equals(root, root2, StringComparison.OrdinalIgnoreCase);
Console.WriteLine(equalIgnoringCase);
// => True

Сравнение порядка сортировки

Чтобы определить порядок сортировки, а не равенство, используйте String.Compare. Он возвращает отрицательное число, ноль или положительное число, указывающее, находится ли первая строка при сортировке перед второй строкой, на той же позиции, что и вторая строка, или после неё:

string first = "Avocado";
string second = "Banana";

// Compare returns a negative number, zero, or a positive number
// to indicate sort order. Specify the comparison type explicitly.
int order = string.Compare(first, second, StringComparison.Ordinal);
Console.WriteLine(order < 0
    ? $"'{first}' sorts before '{second}'."
    : $"'{first}' sorts at or after '{second}'.");
// => 'Avocado' sorts before 'Banana'.

Important

Compare и CompareTo по умолчанию используют сравнение с учетом языковых и региональных параметров, а Equals и ==порядковое сравнение. Чтобы избежать сюрпризов, передайте явное StringComparison значение, чтобы код указывает, какое поведение он хочет.

Сравнение с константами с помощью сопоставления с шаблоном is или switch

Если сравниваемое значение является константой, можно использовать is оператор с константным шаблоном в качестве доступной для чтения альтернативы ==:

string status = "Ready";

// When the right operand is a constant, the is operator and a
// constant pattern read as an alternative to ==.
if (status is "Ready")
{
    Console.WriteLine("The system is ready.");
}
// => The system is ready.

Чтобы сравнить строку с несколькими константами, используйте switch выражение. Каждая ветвь проверяет шаблон-константу, а отбрасывающий шаблон (_) обрабатывает все значения, которые ему не соответствуют. Следующий пример сопоставляет ключевое слово направления с инструкцией по поездке:

foreach (string heading in new[] { "North", "South", "East", "West", "NE" })
{
    // A switch expression matches each constant pattern in turn and
    // returns the first match. The discard (_) handles every other value.
    string instruction = heading switch
    {
        "North" => "Travel due North for 10 km.",
        "South" => "Travel due South for 10 km.",
        "East" => "Travel due East for 10 km.",
        "West" => "Travel due West for 10 km.",
        _ => $"Unknown heading: {heading}.",
    };
    Console.WriteLine(instruction);
}
// => Travel due North for 10 km.
// => Travel due South for 10 km.
// => Travel due East for 10 km.
// => Travel due West for 10 km.
// => Unknown heading: NE.

Выражение switch, которое проверяет строковые константы, выполняет такое же порядковое сравнение с учетом регистра, как и ==, поэтому "north" не соответствует ветви "North".

Выбор правильного сравнения

Выберите тип сравнения на основе данных, а не из привычки:

  • Для идентификаторов, путей к файлам, токенов протокола и другого машинно-определяемого текста используйте Ordinal (или OrdinalIgnoreCase, чтобы игнорировать регистр). Порядковое сравнение выполняется быстро и дает одинаковые результаты в разных культурах.
  • Для текста, который пользователи считывают и сортируют, например имена или названия продуктов, используйте CurrentCulture порядок, соответствующий ожиданиям пользователя.

Сравнение с учетом языковых и культурных особенностей применяет лингвистические правила, которые различаются в зависимости от культуры и могут приводить к неожиданным результатам. Например, в некоторых языках и региональных настройках "ss" и "ß" считаются равными, а порядок строк может различаться на разных компьютерах. Из-за этой изменчивости используйте сравнение с учётом языковых и региональных параметров только для настоящего текста на естественном языке и применяйте один и тот же тип сравнения при сортировке и поиске в коллекции.

Подробное рассмотрение культурно-зависимого сравнения, включая аспекты глобализации и различия между платформами, см. в статье Рекомендации по сравнению строк в .NET.

См. также