Jak sprawić, aby moje porównanie ciągów nie uwzględniało wielkości liter?

111

Stworzyłem program w Javie, aby porównać dwa ciągi:

String s1 = "Hello";
String s2 = "hello";

if (s1.equals(s2)) {
    System.out.println("hai");
} else {
    System.out.println("welcome");
}

Wyświetla „powitanie”. Rozumiem, że wielkość liter ma znaczenie. Ale moim problemem jest to, że chcę porównać dwa ciągi bez rozróżniania wielkości liter. To znaczy oczekuję, że wyjście będzie hai.

user268018
źródło
3
Jeśli wiesz, że rozróżniana jest wielkość liter, przed porównaniem możesz przekonwertować oba na małe lub duże litery.
fastcodejava
jeśli używasz, s1.equalsIgnoreCase(s2)możesz nie zrobić tego wszędzie, gdzie trzeba. Sugeruję, aby znaleźć, skąd pochodzi ciąg - plik, baza danych lub dane wejściowe użytkownika - i przekonwertować je na wielkie (lub małe litery) i nadal używać .equals do porównania.
H2ONaCl
2
Nie konwertuj na małe / wielkie litery (jak sugerują powyższe komentarze), użyj zaakceptowanego equalsIgnoreCasepodejścia. Przeczytaj o tureckim problemie I i podobnych problemach z Unicode dla uzasadnienia.
Ohad Schneider
1
@OhadSchneider i tak equalsIgnoreCasezwraca nieprawidłową wartość dla tureckiego, ponieważ zwraca prawdę przy porównywaniu „i” i „I”, nawet jeśli powinien zwracać fałsz. Więc podejrzewam, że jeśli chcesz wziąć pod uwagę lokalizacje, Collatortak naprawdę jest to najlepszy sposób.
Trejkaz
1
@OhadSchneider Zastanawiam się. Mówi, że zrobienie tego dla każdego znaku daje ten sam wynik, ale wykonanie toLowerCase/ toUpperCasena całym ciągu i zrobienie tego dla każdego znaku daje również dwa różne wyniki.
Trejkaz

Odpowiedzi:

171
  • Najlepiej byłoby użyć s1.equalsIgnoreCase(s2): (patrz javadoc )
  • Możesz także przekonwertować je na wielkie / małe litery i użyć s1.equals(s2)
Michael Bavin
źródło
39
Pamiętaj tylko, że te dwa rozwiązania niekoniecznie są identyczne dla wszystkich lokalizacji. String # equalsIgnoreCase nie używa reguł wielkości liter specyficznych dla ustawień regionalnych, podczas gdy String # toLowerCase i #toUpperCase tak.
jarnbjo
1
@jarnbjo Czy możesz podać przykład, gdzie jest taka różnica?
towi
16
Reguły przypadków specyficzne dla lokalizacji zostały wdrożone przynajmniej dla języka tureckiego i niemieckiego. Turecki traktuje I z kropką i bez kropki jako dwie różne litery, tworząc pary małych i dużych liter iİ i ıI, podczas gdy inne języki traktują iI jako parę i nie używają liter ı i İ. W języku niemieckim mała litera ß jest zapisywana wielką literą jako „SS”.
jarnbjo
24

String.equalsIgnoreCase jest najbardziej praktycznym wyborem do naiwnego porównywania ciągów bez rozróżniania wielkości liter.

Warto jednak mieć świadomość, że ta metoda nie wykonuje pełnego zwijania wielkości liter ani dekompozycji, a więc nie może wykonywać dopasowywania bez liter, jak określono w standardzie Unicode. W rzeczywistości interfejsy API JDK nie zapewniają dostępu do informacji o danych znaków zwijania wielkości liter, więc to zadanie najlepiej delegować do wypróbowanej i przetestowanej biblioteki innej firmy.

Ta biblioteka to ICU , a oto jak można zaimplementować narzędzie do porównywania ciągów bez uwzględniania wielkości liter:

import com.ibm.icu.text.Normalizer2;

// ...

public static boolean equalsIgnoreCase(CharSequence s, CharSequence t) {
    Normalizer2 normalizer = Normalizer2.getNFKCCasefoldInstance();
    return normalizer.normalize(s).equals(normalizer.normalize(t));
}
    String brook = "flu\u0308ßchen";
    String BROOK = "FLÜSSCHEN";

    assert equalsIgnoreCase(brook, BROOK);

Porównanie z naiwny String.equalsIgnoreCase, albo String.equalsna znakami pisanymi dużą lub zamienionymi na wyłącznie małe strun zawiedzie nawet ten prosty test.

(Należy jednak pamiętać, że predefiniowany smak składania wielkości liter getNFKCCasefoldInstancejest niezależny od lokalizacji; w przypadku lokalizacji tureckich UCharacter.foldCasemoże być konieczne trochę więcej pracy ).

połysk
źródło
22

Musisz użyć compareToIgnoreCasemetody Stringobiektu.

int compareValue = str1.compareToIgnoreCase(str2);

if (compareValue == 0)to znaczy str1równa się str2.

Aliti
źródło
10
import java.lang.String; //contains equalsIgnoreCase()
/*
*
*/
String s1 = "Hello";
String s2 = "hello";

if (s1.equalsIgnoreCase(s2)) {
System.out.println("hai");
} else {
System.out.println("welcome");
}

Teraz wyświetli: hai

KNU
źródło
5

W domyślnym API Java masz:

String.CASE_INSENSITIVE_ORDER

Nie ma więc potrzeby przepisywania komparatora, jeśli miałbyś używać łańcuchów z posortowanymi strukturami danych.

String s = "some text here";
s.equalsIgnoreCase("Some text here");

Jest tym, czego potrzebujesz do czystego sprawdzania równości we własnym kodzie.

Tylko dalsze informacje o wszystkim, co dotyczy równości ciągów znaków w Javie. Funkcja hashCode () klasy java.lang.String „rozróżnia wielkość liter”:

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

Więc jeśli chcesz użyć Hashtable / HashMap z ciągami znaków jako kluczami, a klucze takie jak „SomeKey”, „SOMEKEY” i „somekey” są postrzegane jako równe, wtedy będziesz musiał opakować swój ciąg w inną klasę (nie możesz rozszerzać String, ponieważ jest to klasa końcowa). Na przykład :

private static class HashWrap {
    private final String value;
    private final int hash;

    public String get() {
        return value;
    }

    private HashWrap(String value) {
        this.value = value;
        String lc = value.toLowerCase();
        this.hash = lc.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o instanceof HashWrap) {
            HashWrap that = (HashWrap) o;
            return value.equalsIgnoreCase(that.value);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return this.hash;
    }
}

a następnie użyj go jako takiego:

HashMap<HashWrap, Object> map = new HashMap<HashWrap, Object>();
le-doude
źródło
2

Zauważ, że możesz chcieć sprawdzić je również przed wykonaniem swoich .equals lub .equalsIgnoreCase.

Obiekt String o wartości null nie może wywołać metody equals.

to znaczy:

public boolean areStringsSame(String str1, String str2)
{
    if (str1 == null && str2 == null)
        return true;
    if (str1 == null || str2 == null)
        return false;

    return str1.equalsIgnoreCase(str2);
}
VeenarM
źródło
1
Uwaga: drugi dwa stwierdzenia mogą być łączone w celu wytworzenia takiego samego rezultatu: if (str1 == null || str2 == null) return false;.
LuckyMe
Zmodyfikowany kod, aby był bardziej przejrzysty zgodnie z powyższym komentarzem - był to długi dzień :)
VeenarM
1
Możesz również zmienić pierwszą linię, do if (str1 == str2) return true;której oba obsługują wartości null, a także skrócić przypadek, w którym dwa odwołania do ciągu odnoszą się do tego samego obiektu ciągu.
Barney
1

Aby być nullsafe, możesz użyć

org.apache.commons.lang.StringUtils.equalsIgnoreCase(String, String)

lub

org.apache.commons.lang3.StringUtils.equalsIgnoreCase(CharSequence, CharSequence)
brandstaetter
źródło
-6
public boolean newEquals(String str1, String str2)
{
    int len = str1.length();
int len1 = str2.length();
if(len==len1)
{
    for(int i=0,j=0;i<str1.length();i++,j++)
    {
        if(str1.charAt(i)!=str2.charAt(j))
        return false;
    }`enter code here`
}
return true;
}
javacoder
źródło