Есть ли в C # нечувствительный к регистру оператор равенства?


156

Я знаю, что следующее учитывает регистр:

if (StringA == StringB) {

Так есть ли оператор, который будет сравнивать две строки нечувствительным образом?


возможный дубликат сравнения Caselessly в C #
nawfal

Если кто-то сталкивается с этим вопросом в поисках нечувствительного к регистру сравнения для словаря <string, int>, взгляните на этот вопрос здесь: Независимый от регистра доступ к универсальному словарю
Robotnik

Это было бы действительно хорошо; скажем, для определения соответствующей ~=параллели ==в качестве версии без учета регистра.
Eidylon

Если разработчики Microsoft увидят это, я думаю, что в следующей версии csharp нужен оператор без учета регистра. Эта строка.Equal () длинная.
Rez.Net

Ответы:


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 раз быстрее ! Это также более надежно, чем преобразование строк в нижний или верхний регистр (см. Проблему Turkish i). Поэтому всегда используйте метод String.Equals для сравнения строк на равенство:

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

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

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

Обратите внимание, что во втором примере используется логика сравнения строк текущей культуры, что делает его медленнее, чем сравнение «порядкового игнорирования регистра» в первом примере, поэтому если вам не нужна логика сравнения строк для конкретной культуры, и вы после максимальной производительности используйте сравнение «порядковый номер игнорирования».

Для получения дополнительной информации прочитайте полную историю в моем блоге .


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

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

3
«Большинство из нас знают об этих потенциальных проблемах, многие учебники по Интернету приводят турецкое« я »в качестве примера», - мало людей, и вы все еще упоминаете это как второе предложение в своем ответе. Кроме того, ваш ответ не содержит достаточного обоснования, чтобы никогда не использовать его - вы просто упоминаете производительность; производительность не всегда является конечным приоритетом. В результате вы в настоящее время нарушаете правила справочного центра; ссылки на внешние сайты в порядке, но вы недостаточно суммировали содержание (турецкая проблема «i»). ТАК не ваша рекламная площадка.
антидух

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

как предположил Джон

РЕДАКТИРОВАТЬ: исправлена ​​ошибка



3

Оператор? НЕТ, но я думаю, что вы можете изменить свою культуру, чтобы сравнение строк не учитывало регистр.

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

Я уверен, что это изменит способ сравнения строк оператором equals.


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

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

В то время как я , как чистый ищут синтаксис использования, это немного вводит в заблуждении ( IgnoreCaseпротив IgnoreCaseString) и неоднозначной (Java выбирает неявную распаковку против неявного бокса , поэтому я считаю , это не будет работать в Java с неявными литом обратно в строку в там). И это создает накладные расходы памяти 2 новых объектов с выполнением дерева вызовов для каждого сравнения, перепрыгивая через несколько вложенных вызовов методов для отображаемого варианта использования. Тем не менее, в большинстве случаев производительность, вероятно, достаточно хорошая.
Arkaine55

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

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

Просто отметьте, что вам нужно будет проверить наличие нуля thisStringдо вызова ext.


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
С точки зрения производительности это не очень хорошее решение, так как здесь вы также создадите 2 новых экземпляра строки.
Фредерик Гейселс

0

Другие ответы здесь полностью действительны, но каким-то образом требуется некоторое время, чтобы напечатать, StringComparison.OrdinalIgnoreCaseа также использовать String.Compare.

Я написал простой метод расширения String, в котором вы можете указать, является ли сравнение чувствительным к регистру или бессмысленным с логическим значением - см. Следующий ответ:

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.