Як зробити так, щоб випадок порівняння рядків був нечутливим?


111

Я створив програму Java для порівняння двох рядків:

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

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

На ньому відображається "привітання". Я розумію, що це враховує регістри. Але моя проблема полягає в тому, що я хочу порівняти два рядки без чутливості до регістру. Тобто я очікую, що результат буде hai.


3
Якщо ви знаєте, що це чутливе до регістру, перед порівнянням ви можете перетворити і малі, або великі регістри.
fastcodejava

якщо ви використовуєте, s1.equalsIgnoreCase(s2)ви можете зробити це не скрізь, коли це потрібно зробити. Я пропоную вам знайти, звідки походить рядок - файл, базу даних чи введення користувача, - і перетворити їх у верхній (або нижній регістр) та продовжувати використовувати .equals для порівняння.
H2ONaCl

2
Не перетворюйте на малі / великі регіони (як це запропоновано коментарями вище), використовуйте прийнятий equalsIgnoreCaseпідхід. Прочитайте про турецьку проблему I та подібні проблеми Unicode для обґрунтування.
Охад Шнайдер

1
@OhadSchneider все одно equalsIgnoreCaseповертає неправильне значення для турецької мови, оскільки воно повертає істинне для порівняння "i" та "I", хоча воно повинно повертати помилкове. Тож я підозрюю, що якщо ви хочете взяти до уваги локалі, Collatorто насправді це шлях.
Трежказ

1
@OhadSchneider Цікаво. Це говорить про те, що виконання цього символу дає один і той же результат, але виконання toLowerCase/ toUpperCaseв цілому рядку і виконання цього символу дають два різні результати.
Трежказ

Відповіді:


171
  • Найкраще було б використовувати s1.equalsIgnoreCase(s2): (див. Javadoc )
  • Ви також можете конвертувати їх як у верхній / нижній регістр, так і використовувати s1.equals(s2)

39
Тільки майте на увазі, що ці два рішення необов'язково однакові для всіх місцевостей. String # equalsIgnoreCase не використовує конкретні правила обкладинки, а String # toLowerCase і #toUpperCase.
jarnbjo

1
@jarnbjo Чи можете ви навести приклад, де така різниця?
буксі

16
Правила, що стосуються місцевих ситуацій, принаймні застосовуються для турецької та німецької мов. Турецька трактує я з і без крапки як дві різні літери, створюючи пари нижнього / верхнього регістру iİ і ıI, тоді як інші мови ставляться до iI як до пари і не використовують літери ı і İ. У німецькій мові нижній регістр ß пишеться з великої літери як "SS".
jarnbjo


24

String.equalsIgnoreCase є найбільш практичним вибором для порівняння наївних, нечутливих до випадків, рядків.

Однак добре пам’ятати, що цей метод не робить ні повного складання, ні декомпозиції, і тому не може виконувати безвідкладне зіставлення, як зазначено в стандарті Unicode. Насправді API JDK не надають доступу до інформації про складені символи даних про складання корпусу, тому цю роботу найкраще делегувати випробуваній сторонній бібліотеці.

Ця бібліотека є ICU , і ось як можна реалізувати утиліту для порівняння рядків, нечутливих до регістру:

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

Наївне порівняння з String.equalsIgnoreCase, або String.equalsна прописні або нижній регістр рядків не вийде навіть цей простий тест.

(Зверніть увагу, що попередньо визначений смак складання корпусу не getNFKCCasefoldInstanceзалежить від місцевості; для турецьких місцевостей UCharacter.foldCaseможе знадобитися трохи більше роботи .)


22

Ви повинні використовувати compareToIgnoreCaseметод Stringоб’єкта.

int compareValue = str1.compareToIgnoreCase(str2);

if (compareValue == 0)це означає, що str1дорівнює str2.


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

Тепер він виведе: hai


5

У Java API за замовчуванням у вас є:

String.CASE_INSENSITIVE_ORDER

Тому вам не потрібно переписувати компаратор, якщо ви використовували б рядки зі сортованими структурами даних.

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

Це те, що потрібно для чистої перевірки рівності у власному коді.

Просто для отримання додаткової інформації про все, що стосується рівності струн на Java. Функція hashCode () класу java.lang.String "відрізняється від регістру":

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

Отже, якщо ви хочете використовувати Hashtable / HashMap з рядками як ключі, а ключі типу "SomeKey", "SOMEKEY" та "somekey" вважати рівними, вам доведеться перевести рядок в інший клас (ви не можете продовжувати Рядок, оскільки це заключний клас). Наприклад :

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

а потім використовувати його як таке:

HashMap<HashWrap, Object> map = new HashMap<HashWrap, Object>();

2

Зауважте, що ви, можливо, захочете виконати їхні нульові перевірки також перед тим, як робити .equals або .equalsIgnoreCase.

Об'єкт null String не може викликати метод equals.

тобто:

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

1
Примітка: другий дві заяви можуть бути об'єднані , щоб зробити такий же результат , як цей: if (str1 == null || str2 == null) return false;.
LuckyMe

Змінений код, щоб бути чистішим, як зазначено вище, коментар - був довгий день :)
VeenarM

1
Ви також можете змінити перший рядок, if (str1 == str2) return true;який обидва забезпечує нулі, а також ярлик випадку, коли дві посилання рядків посилаються на один і той же рядковий об'єкт.
Барні




1

Щоб бути безпечним, ви можете використовувати

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

або

org.apache.commons.lang3.StringUtils.equalsIgnoreCase(CharSequence, CharSequence)

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