Сравнение Строк Сложнее, Чем Кажется. Начало
Сравнение строк отличается от сравнения чисел. 2 числа равны, если их значения идентичны. Для строк всё сложнее. Например, вы хотите сравнение с учетом регистра? А как насчёт разных способов написать одну и ту же букву? Например, в немецком языке часто встречается буква
ß, но её также можно написать ss, так как это проще на многих клавиатурах.В .NET есть 6 режимов сравнения строк:
1. Ordinal: выполняет простое сравнение байтов независимо от языка. Наиболее удобен для сравнения ресурсов, чувствительных к регистру, таких как пароли.
2. OrdinalIgnoreCase: обрабатывает символы в строках для сравнения, как если бы они были преобразованы в верхний регистр с использованием соглашений инвариантного языка и региональных параметров, а затем выполняет простое сравнение байтов, которое не зависит от языка. Подходит для сравнения строк, созданных программно, когда регистр не важен, таких как пути и имена файлов.
3. InvariantCulture: сравнивает строки лингвистически релевантным способом, но он не подходит для отображения в какой-либо конкретной культуре. Основное применение - упорядочивание строк таким образом, чтобы оно было идентичным для разных культур.
4. InvariantCultureIgnoreCase: сравнивает строки лингвистически релевантным способом, игнорируя регистр.
5. CurrentCulture: может использоваться, когда строки имеют лингвистическое значение (например, отображаются для пользователя или являются результатом взаимодействия с пользователем).
6. CurrentCultureIgnoreCase: может использоваться, когда строки являются лингвистически релевантными, а их регистр - нет.
Важно явно указывать режим сравнения, чтобы избежать неожиданного поведения или, что еще хуже, проблем с безопасностью. Например, если вы сравните 2 пароля, используя текущую культуру или инвариантную культуру, пароли могут быть одинаковыми, тогда как на самом деле они могут быть разными!
Примеры
Понять разницу между Ordinal и OrdinalIgnoreCase довольно просто. Сложнее понять культурно-зависимые сравнения.
Примечание: В примерах ниже последним параметром идёт перечисление
StringComparison. Для краткости я оставил лишь значение перечисления, поэтому подразумевается, что везде используется using static System.StringComparison;
// false
string.Equals("ss", "ß", OrdinalIgnoreCase);
// true в Windows / false в Linux (WSL) – об этом позже
string.Equals("ss", "ß", InvariantCulture);
\0 и ещё некоторые символы игнорируются в лингвистических сравнениях:// falseСравнение с учетом культуры сравнивает графемы. Например, строка
string.Equals("a\0b", "ab", Ordinal);
// true
string.Equals("a\0b", "ab", InvariantCulture);
// true
string.Equals("a\0b", "ab", CurrentCulture);
A\r\nB разделена на A, \r\n и B. Это означает, что \n не является частью этой строки. Но \r или \r\n являются частью строки:// trueПродолжение следует…
"A\r\nB".Contains("\n", Ordinal);
// false
"A\r\nB".Contains("\n", InvariantCulture);
//true
"A\r\nB".Contains("\r", InvariantCulture);
Источник: https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm