Чи є оператор C # нечутливий дорівнює оператору?


156

Я знаю, що до регістру належить:

if (StringA == StringB) {

Так чи є оператор, який порівняє два рядки нечутливим чином?



Якщо хтось наткнеться на це запитання, шукаючи порівняння з невідчутними для Словника словника <string, int>, перегляньте це питання тут: Нечутливий доступ до справи для загального словника
Robotnik

Було б справді приємно; скажімо, щоб визначити відповідну ~=паралелі ==як нечутливу до регістру версію.
ейдилон

Якщо розробники Microsoft бачать це, я думаю, що в наступній версії csharp необхідний оператор, нечутливий до регістру. Цей рядок.Equal () є тривалим.
Рез.Нет

Відповіді:


288

Спробуйте це:

string.Equals(a, b, StringComparison.CurrentCultureIgnoreCase);

Я родич StackOverflow - я можу пояснити, що ви маєте на увазі, додавши посилання? Ви маєте на увазі документи MSDN?
Джон Фемінелла

55
Якщо ви хочете порівняти з культурою порівняння, використовуйте цей метод. Якщо ви просто хочете переконатися, що "FILE" і "file" прийняті, скористайтеся "OrdinalIgnoreCase", інакше ваш код може не працювати в таких місцях, як турецькі мови. Більше інформації див. У розділі moserware.com/2008/02/does-your-code-pass-turkey-test.html
Джефф Мозер

10
Не впевнений, про що говорить Самуїл ... ця відповідь ідеальна. Його правильне і самоосмислене. Він не потребує посилань. +1
Плавання по дзюдо

3
Аарг, це така жахлива рота! моя клавіатура зношується Пройшли дні, коли я можу використовувати " if A$=B$ then goto 10"
Санджай Манохар

9
@Sanjay Manohar Потім напишіть спеціальний оператор - і я рекомендую кращу клавіатуру.
Rushyo

37

Кращий спосіб порівняти 2 рядки без урахування регістру літер є використання String.equals статичний метод , який визначає ордінала ігнорувати порівняння разі рядки. Це також найшвидший спосіб, набагато швидший, ніж перетворення рядків у нижній чи верхній регістр та порівняння їх після цього.

Я перевірив ефективність обох підходів, і порівняння рядкових випадків порядкового ігнорування було більш ніж у 9 разів швидше ! Це також надійніше, ніж перетворення рядків у нижній чи верхній регістр (ознайомтеся з проблемою турецького i). Тому завжди використовуйте метод String.Equals для порівняння рядків для рівності:

String.Equals(string1, string2, StringComparison.OrdinalIgnoreCase);

Якщо ви хочете виконати порівняння рядків, характерних для культури, ви можете використовувати наступний код:

String.Equals(string1, string2, StringComparison.CurrentCultureIgnoreCase);

Зверніть увагу, що другий приклад використовує логіку порівняння рядків у поточній культурі, що робить його повільніше порівняння зі «порядковим випадком ігнорування» у першому прикладі, тому якщо вам не потрібна якась специфічна для культури логіка порівняння рядків, і ви після максимальної продуктивності використовуйте порівняння «порядкового ігнорування випадку».

Для отримання додаткової інформації читайте повну історію в моєму блозі .


1
Не пропонуйте ToLowerабо ToLowerInvariant: вони створюють пам'ять просто для порівняння, і вони можуть не працювати, оскільки нові набори символів додаються до unicode. ToUpperне вдається через турецьке «я», серед інших; немає жодних причин, чому б в ToLowerмайбутньому не вийшло з подібних причин.
антидух

@antiduh, дякую за ваш коментар. Більшість із нас знають про ці потенційні проблеми, багато навчальних посібників в Інтернеті дають приклад турецького «я». Як ви бачите у своєму дописі, я не рекомендую використовувати ToLowerчи ToLowerInvariantметоди, я просто хотів показати, наскільки ефективніший String.Equalsметод.
Павло Владов

3
"Більшість із нас знають про ці потенційні проблеми. Багато навчальних посібників в Інтернеті дають турецьке" я "як приклад" - людей недостатньо, і ви все ще згадуєте це як друге речення у своїй відповіді. Крім того, ваша відповідь не містить достатнього обґрунтування, щоб ніколи її не використовувати - ви просто згадуєте про ефективність; виконання не завжди є першочерговим завданням. Як наслідок, Ви зараз порушуєте правила довідкового центру; посилання на зовнішні веб-сайти чудові, але ви недостатньо не узагальнили вміст (проблема "я"). Так не ваша рекламна платформа.
антидух

20

Існує ряд властивостей StringComparerстатичного класу, які повертають порівняльники для будь-якого типу чутливості до регістру, який ви хочете:

StringComparer Властивості

Наприклад, ви можете зателефонувати

StringComparer.CurrentCultureIgnoreCase.Equals(string1, string2)

або

StringComparer.CurrentCultureIgnoreCase.Compare(string1, string2)

Це трохи чистіше, ніж аргументи string.Equalsабо string.Compareперевантаження, які беруть StringComparisonаргументи.


15
System.Collections.CaseInsensitiveComparer

або

System.StringComparer.OrdinalIgnoreCase

Чи впливає це на всю заявку?
GateKiller

3
Де я можу знайти більше інформації про це. Чи означає це, що я можу використовувати == для невідчутливого до випадку відповідності?
GateKiller


8

або

if (StringA.Equals(StringB, StringComparison.CurrentCultureIgnoreCase)) {

але вам потрібно бути впевненим, що StringA не є нульовим. Тому, ймовірно, краще використовувати ту:

string.Equals(StringA , StringB, StringComparison.CurrentCultureIgnoreCase);

як запропонував Джон

EDIT: виправлено помилку



3

Оператор? НІ, але я думаю, ви можете змінити свою культуру, щоб порівняння рядків не залежно від регістру.

// you'll want to change this...
System.Threading.Thread.CurrentThread.CurrentCulture
// and you'll want to custimize this
System.Globalization.CultureInfo.CompareInfo

Я впевнений, що це змінить спосіб порівняння рядків оператором рівних.


Так, принаймні, це зовсім не те, що ви хотіли б зробити, якщо ви не хочете, щоб всі порівняння рядків були нечутливими до регістру. Але я думаю, це змінює поведінку оператора рівних.
Джон Лейдегрен

3

Ось ідея спростити синтаксис:

public class IgnoreCase
{
    private readonly string _value;

    public IgnoreCase(string s)
    {
        _value = s;
    }

    protected bool Equals(IgnoreCase other)
    {
        return this == other;
    }

    public override bool Equals(object obj)
    {
        return obj != null &&
               (ReferenceEquals(this, obj) || (obj.GetType() == GetType() && this == (IgnoreCase) obj));
    }

    public override int GetHashCode()
    {
        return _value?.GetHashCode() ?? 0;
    }

    public static bool operator ==(IgnoreCase a, IgnoreCase b)
    {
        return string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
    }

    public static bool operator !=(IgnoreCase a, IgnoreCase b)
    {
        return !(a == b);
    }

    public static implicit operator string(IgnoreCase s)
    {
        return s._value;
    }

    public static implicit operator IgnoreCase(string s)
    {
        return new IgnoreCase(s);
    }
}

Використовується як:

Console.WriteLine((IgnoreCase) "a" == "b"); // false
Console.WriteLine((IgnoreCase) "abc" == "abC"); // true
Console.WriteLine((IgnoreCase) "Abc" == "aBc"); // true
Console.WriteLine((IgnoreCase) "ABC" == "ABC"); // true

Хоча мені подобається синтаксис чистого вигляду використання, він дещо вводить в оману ( IgnoreCasevs IgnoreCaseString) та неоднозначно (Java вибирає неявне розпакування та неявне боксування, тому я вважаю, що це не буде працювати в Java з неявним відкиданням до рядка). І це створює обсяг пам'яті 2 нових об'єктів із виконанням дерева викликів для кожного порівняння, перескакуючи на кілька вкладених викликів методу для відображеного випадку використання. Однак, для більшості випадків продуктивність, мабуть, досить хороша.
Arkaine55

Хоча це розумна ідея, вона не дуже розумна з точки зору ремонту. Ви ефективно створюєте сурогатний тип рядка замість того, щоб використовувати вбудований тип рядка системи. Програміст, що приходить, після цього не зрозуміє, що відбувається з першого погляду, і тоді він / вона придивиться до вас. Використання string.Equals () насправді не все так погано, і більшість людей зрозуміють, що це робить.
ntcolonel

1

Я так звик друкувати в кінці цих методів порівняння: , StringComparison.

Тому я зробив продовження.

namespace System
{   public static class StringExtension
    {
        public static bool Equals(this string thisString, string compareString,
             StringComparison stringComparison)
        {
            return string.Equals(thisString, compareString, stringComparison);
        }
    }
}

Просто зауважте, що вам потрібно буде перевірити наявність "null" thisStringперед тим, як викликати доб.


1
Це те саме, що і цей вбудований метод у поточних версіях .NET Framework? docs.microsoft.com/en-gb/dotnet/api/…
Бернар Вандер Став

1
Виявляється так. Схоже, новіші версії .net включають це зараз.
Валамас

Доступний з .NET 4.5 та всіх версій .NET Core.
Бернард Вандер Став


0
if (StringA.ToUpperInvariant() == StringB.ToUpperInvariant()) {

Люди повідомляють, що ToUpperInvariant () швидше, ніж ToLowerInvariant ().


1
Інваріант може бути поганою ідеєю, якщо в нинішній чи бажаній культурі є спеціальні правила щодо верхнього корпусу.
OregonGhost

Чи створює це нову копію кожного рядка? Якщо так, погана ідея.
cjk

1
Це також призведе до виключення, якщо будь-який (або обидва) рядки є недійсними.
tvanfosson

3
Це означає, що це не дуже вдале рішення, оскільки ви також створите два нові рядкові екземпляри і тут.
Фредерік Гейселс

0

Інші відповіді тут цілком справедливі, але певний час, StringComparison.OrdinalIgnoreCaseа також використання потрібен певний час String.Compare.

Я зашифрував простий метод розширення String, де ви могли вказати, чи порівняння є регістровим чи регістровим без букву з булевим - див. Наступну відповідь:

https://stackoverflow.com/a/49208128/2338477

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.