Перейти к содержимому
Теория · Строки · урок 7 из 10

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

Возьмите два чека из одного магазина за одну и ту же покупку. Это два разных листка бумаги, но напечатано на них одно и то же. Одинаковые ли это чеки? Всё зависит от вопроса: «это один листок?» или «на них один текст?».

Для строк C# отвечает на второй вопрос. Оператор == сравнивает текст, символ за символом, даже когда строки — два разных объекта:

C#
string a = "cat";
string b = "CAT".ToLower();
Console.WriteLine(a == b);     // True
Console.WriteLine(a == "Cat"); // False
Console.WriteLine(a != "dog"); // True

Давайте разберём пример по строкам:

  1. "CAT".ToLower() создаёт при запуске новую строку "cat". В памяти теперь две строки с одинаковым текстом, как два листка с одинаковой покупкой.
  2. a == b сравнивает их посимвольно и печатает True.
  3. a == "Cat" печатает False: 'c' и 'C' — разные символы, и регистр букв важен.
  4. != даёт обратный ответ: строка "cat" не равна "dog", поэтому печатается True.

== сравнивает текст

Для массивов == проверяет, один ли это массив, и два массива с одинаковыми элементами не равны. У строк == устроен иначе: он сравнивает длину и затем каждый символ. Поэтому строки с одинаковым текстом равны, откуда бы они ни взялись.

Здесь картинка с чеками неточна. Две строки с одинаковым текстом иногда оказываются одним объектом: одинаковые литералы, как вы видели, хранятся как одна строка. Для == это неважно: он сравнивает текст в любом случае.

Этот вопрос часто задают на собеседовании: что сравнивает == у строк? В C# — текст, хотя string и ссылочный тип. А в Java == для строк сравнивает ссылки, так что ответ зависит от языка.

Без учёта регистра

Пользователь ответил YES, а программа ждёт yes. По смыслу ответы одинаковые, но == их различает. Для сравнения без учёта регистра у строки есть метод Equals со вторым аргументом — способом сравнения:

C#
string answer = "YES";
Console.WriteLine(answer == "yes"); // False
Console.WriteLine(answer.Equals("yes", StringComparison.OrdinalIgnoreCase)); // True

StringComparison.OrdinalIgnoreCase

Название способа складывается из двух частей:

  • Ordinal — сравнивать символ с символом как есть, без правил какого-либо языка;
  • IgnoreCase — считать заглавную и строчную букву одинаковыми.

Тот же способ сравнения можно передать и в string.Equals(answer, "yes", StringComparison.OrdinalIgnoreCase): там обе строки идут аргументами.

Почему не ToLower

Можно привести обе строки к строчным буквам: answer.ToLower() == "yes". Это тоже работает, но у такого способа два минуса:

  • каждый ToLower создаёт новую строку, хотя нужен только ответ true или false;
  • ToLower и ToUpper следуют региональным настройкам компьютера. С турецкими настройками "TITLE".ToLower() даёт "tıtle" с буквой ı без точки, и сравнение с "title" вернёт false.

OrdinalIgnoreCase от настроек не зависит и новых строк не создаёт.

Пустая строка

Пустая строка "" — строка без символов, её длина 0. Проверить, пуста ли строка, можно сравнением s == "" или через s.Length == 0. Чаще пишут string.IsNullOrEmpty(s): этот метод возвращает true и для пустой строки, и для значения null, которое означает, что строки нет вовсе.

Пробел тоже символ, поэтому строка " " не пустая. Если строку из одних пробелов нужно считать пустой, есть string.IsNullOrWhiteSpace:

C#
string empty = "";
string space = " ";
Console.WriteLine(empty.Length);                     // 0
Console.WriteLine(string.IsNullOrEmpty(empty));      // True
Console.WriteLine(string.IsNullOrEmpty(space));      // False
Console.WriteLine(string.IsNullOrWhiteSpace(space)); // True

Частая ошибка

Строки попробовали сравнить по алфавиту знаком <:

C#
string a = "apple";
string b = "banana";
Console.WriteLine(a < b); // ✗
TEXT
error CS0019: Operator '<' cannot be applied to operands of type 'string' and 'string'

Программа не компилируется: операторы < и > работают с числами и символами, а со строками — нет. Порядок строк узнают через string.Compare(a, b). Метод возвращает:

  • отрицательное число, если a стоит по алфавиту раньше b;
  • ноль для равных строк;
  • положительное число, если a стоит позже.

Здесь string.Compare(a, b) < 0 даёт true.

Главное

  • == и != сравнивают текст строк посимвольно и с учётом регистра, даже если это два разных объекта.
  • Без учёта регистра сравнивают через Equals со StringComparison.OrdinalIgnoreCase, а не через ToLower.
  • string.IsNullOrEmpty проверяет пустую строку и null, а string.IsNullOrWhiteSpace ещё и строку из пробелов.
  • < и > для строк не компилируются; порядок по алфавиту даёт string.Compare.