Czy istnieje operator równości bez uwzględniania wielkości liter w języku C #?

156

Wiem, że rozróżniana jest wielkość liter:

if (StringA == StringB) {

Czy jest więc operator, który porównuje dwa ciągi w niewrażliwy sposób?

GateKiller
źródło
możliwy duplikat porównania ciągów Caselessly w C #
nawfal
W przypadku gdy ktoś potyka się na to pytanie szuka przypadku niewrażliwych porównania dla Dictionary <string, int>, rzucić okiem na to pytanie tutaj: wielkość liter ma znaczenie dostępu rodzajowe słownika
Robotnik
Byłoby naprawdę miło; powiedzmy, aby zdefiniować odpowiednik ~=równoległy ==jako wersję bez uwzględniania wielkości liter.
eidylon
Jeśli programiści firmy Microsoft to zobaczą, myślę, że w następnej wersji csharp potrzebny jest operator bez rozróżniania wielkości liter. Ten ciąg.Equal () jest długi.
Rez.Net

Odpowiedzi:

288

Spróbuj tego:

string.Equals(a, b, StringComparison.CurrentCultureIgnoreCase);
John Feminella
źródło
Jestem względnym nowicjuszem StackOverflow - czy możesz wyjaśnić, co masz na myśli, dodając link? Czy masz na myśli dokumenty MSDN?
John Feminella,
55
Jeśli chcesz porównać kulturę, użyj tej metody. Jeśli chcesz się tylko upewnić, że zarówno „FILE”, jak i „file” są akceptowane, użyj „OrdinalIgnoreCase” lub Twój kod może nie działać w miejscach takich jak tureckie lokalizacje. Więcej informacji można znaleźć na stronie moserware.com/2008/02/does-your-code-pass-turkey-test.html
Jeff Moser
10
Nie jestem pewien, o czym mówi Samuel ... ta odpowiedź jest idealna. Jego poprawne i oczywiste. Nie potrzebuje żadnych odniesień. +1
Sailing Judo
3
Ach, to taki okropny kęs! moja klawiatura się zużyje. Dawno minęły czasy, kiedy mogę używać „ if A$=B$ then goto 10
Sanjay Manohar
9
@Sanjay Manohar W takim razie napisz niestandardowy operator - poleciłbym lepszą klawiaturę.
Rushyo
37

Najlepszym sposobem porównania 2 łańcuchów ignorujących wielkość liter jest użycie statycznej metody String.Equals , określającej porównanie ciągów liczb porządkowych ignorujących wielkość liter . Jest to również najszybszy sposób, znacznie szybszy niż konwersja ciągów znaków na małe lub duże litery i ich późniejsze porównywanie.

Przetestowałem wydajność obu podejść i porównanie ciągów znaków porządkowych ignorujących wielkość liter było ponad 9 razy szybsze ! Jest również bardziej niezawodny niż konwersja ciągów znaków na małe lub duże litery (sprawdź turecki problem i). Dlatego zawsze używaj metody String.Equals , aby porównać ciągi znaków dla równości:

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

Jeśli chcesz przeprowadzić porównanie ciągów specyficznych dla kultury, możesz użyć następującego kodu:

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

Zwróć uwagę, że w drugim przykładzie użyto logiki porównywania ciągów bieżącej kultury, co powoduje, że jest ona wolniejsza niż porównanie „porządkowego ignorowania wielkości liter” w pierwszym przykładzie, więc jeśli nie potrzebujesz żadnej logiki porównywania ciągów specyficznej dla kultury i jesteś po osiągnięciu maksymalnej wydajności użyj porównania „porządkowa ignoruj ​​wielkość liter”.

Aby uzyskać więcej informacji, przeczytaj całą historię na moim blogu .

Pavel Vladov
źródło
1
Nie sugeruj ToLowerani ToLowerInvariant: tworzą pamięć tylko w celu wykonania porównania i mogą się nie powieść, gdy nowe zestawy znaków zostaną dodane do Unicode. ToUpperzawodzi z powodu m.in. tureckiego „i”; nie ma powodu, dla którego ToLowernie zawiedzie w przyszłości z podobnych powodów.
antiduh
@antiduh, dziękuję za komentarz. Większość z nas jest świadoma tych potencjalnych problemów, wiele tutoriali w Internecie podaje tureckie „i” jako przykład. Jak widzicie w moim poście, nie polecam stosowania ToLowerani ToLowerInvariantmetod, chciałem tylko pokazać, o ile wydajniejsza jest ta String.Equalsmetoda.
Pavel Vladov
3
„Większość z nas zdaje sobie sprawę z tych potencjalnych problemów, wiele samouczków w Internecie podaje tureckie„ i ”jako przykład” - nie ma wystarczającej liczby osób, a Ty nadal wymieniasz to jako drugie zdanie w swojej odpowiedzi. Co więcej, twoja odpowiedź nie zawiera wystarczającego uzasadnienia, aby nigdy jej nie używać - po prostu wspomnij o wydajności; wydajność nie zawsze jest najwyższym priorytetem. W rezultacie obecnie naruszasz wytyczne Centrum pomocy; linki do zewnętrznych witryn są w porządku, ale nie podsumowałeś wystarczająco zawartości (problem z tureckim „i”). SO nie jest Twoją platformą reklamową.
antiduh
20

Istnieje wiele właściwości w StringComparerklasie statycznej, które zwracają elementy porównujące dla dowolnego typu rozróżniania wielkości liter:

StringComparer Nieruchomości

Na przykład możesz zadzwonić

StringComparer.CurrentCultureIgnoreCase.Equals(string1, string2)

lub

StringComparer.CurrentCultureIgnoreCase.Compare(string1, string2)

Jest trochę czystszy niż przeciążenia string.Equalslub, string.Comparektóre przyjmują StringComparisonargument.

Ryan Lundy
źródło
15
System.Collections.CaseInsensitiveComparer

lub

System.StringComparer.OrdinalIgnoreCase
leppie
źródło
Czy ma to wpływ na całą aplikację?
GateKiller,
3
Gdzie mogę znaleźć więcej informacji na ten temat. Czy to oznacza, że ​​mogę użyć == dla dopasowania bez rozróżniania wielkości liter?
GateKiller
9
string.Equals(StringA, StringB, StringComparison.CurrentCultureIgnoreCase);
Erick
źródło
8

lub

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

ale musisz mieć pewność, że StringA nie jest null. Więc prawdopodobnie lepiej użyć:

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

jak zasugerował John

EDYCJA: poprawiono błąd

Grzenio
źródło
4

Możesz użyć

if (stringA.equals(StringB, StringComparison.CurrentCultureIgnoreCase))
Andy Mikula
źródło
3

Operator? NIE, ale myślę, że możesz zmienić swoją kulturę, aby w porównaniu ciągów nie była rozróżniana wielkość liter.

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

Jestem przekonany, że zmieni to sposób porównywania ciągów znaków przez operator równości.

John Leidegren
źródło
Tak, delikatnie mówiąc, absolutnie nie jest to to, co chciałbyś zrobić, chyba że chcesz, aby wszystkie porównania ciągów były niewrażliwe na wielkość liter. Ale myślę, że zmienia to zachowanie operatora równości.
John Leidegren
3

Oto pomysł na uproszczenie składni:

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

Użyteczne jak:

Console.WriteLine((IgnoreCase) "a" == "b"); // false
Console.WriteLine((IgnoreCase) "abc" == "abC"); // true
Console.WriteLine((IgnoreCase) "Abc" == "aBc"); // true
Console.WriteLine((IgnoreCase) "ABC" == "ABC"); // true
renouve
źródło
Chociaż podoba mi się czysto wyglądająca składnia użycia, jest nieco myląca ( IgnoreCasevs IgnoreCaseString) i niejednoznaczna (Java wybiera niejawne rozpakowywanie vs niejawne pakowanie, więc uważam , że nie zadziałaoby to w Javie z niejawnym rzutowaniem z powrotem na ciąg). Tworzy to narzut pamięci na 2 nowe obiekty, a wykonanie drzewa wywołań dla każdego porównania przeskakuje do kilku zagnieżdżonych wywołań metod dla wyświetlanego przypadku użycia. To powiedziawszy, w większości przypadków wydajność jest prawdopodobnie wystarczająco dobra.
Arkaine55
Chociaż jest to sprytny pomysł, nie jest tak naprawdę mądry z punktu widzenia możliwości konserwacji. Skutecznie tworzysz zastępczy typ ciągu zamiast korzystać z wbudowanego w system typu ciągu. Programista, który przychodzi później, na pierwszy rzut oka nie zrozumie, co się dzieje, a potem przeklnie cię. Używanie string.Equals () nie jest wcale takie złe i większość ludzi zrozumie, co robi.
ntcolonel
1

Jestem przyzwyczajony do pisania na końcu tych metod porównawczych: , StringComparison.

Więc zrobiłem rozszerzenie.

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

Zwróć uwagę, że thisStringprzed wywołaniem rozszerzenia ext.

Valamas
źródło
1
Czy to jest to samo, co ta wbudowana metoda w aktualnych wersjach .NET Framework? docs.microsoft.com/en-gb/dotnet/api/…
Bernard Vander Beken
1
Tak się wydaje. Wygląda na to, że nowsze wersje .net zawierają to teraz.
Valamas
Dostępne od wersji .NET 4.5 i wszystkich wersji .NET Core.
Bernard Vander Beken
0
string.Compare(string1, string2, true)
user25623
źródło
0
if (StringA.ToUpperInvariant() == StringB.ToUpperInvariant()) {

Ludzie zgłaszają, że ToUpperInvariant () jest szybsze niż ToLowerInvariant ().

KNOOPX
źródło
1
Niezmiennik może być złym pomysłem, jeśli aktualna lub pożądana kultura ma specjalne zasady dotyczące wielkich liter.
OregonGhost,
Czy to tworzy nową kopię każdego ciągu? Jeśli tak, to zły pomysł.
cjk
1
Spowoduje to również zgłoszenie wyjątku, jeśli jeden (lub oba) ciągi mają wartość null.
tvanfosson
3
Pod względem wydajności nie jest to dobre rozwiązanie, ponieważ utworzysz tutaj również 2 nowe instancje stringów.
Frederik Gheysels
0

Inne odpowiedzi są tutaj całkowicie poprawne, ale w jakiś sposób wpisanie StringComparison.OrdinalIgnoreCasei użycie zajmuje trochę czasu String.Compare.

Zakodowałem prostą metodę rozszerzenia String, w której możesz określić, czy porównanie ma rozróżniać wielkość liter, czy bez sensu z wartością logiczną - zobacz następującą odpowiedź:

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

TarmoPikaro
źródło