Как сделать сравнение строк без учета регистра?


1057

Как выполнить сравнение строк без учета регистра в JavaScript?


25
см. недавно добавленный .localeCompare()метод javascript. Поддерживается только современными браузерами на момент написания (IE11 +). см. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Адриен Бе


5
@AdrienBe "A".localeCompare( "a" );возвращается 1в консоли Chrome 48.
Мануэль

3
@manuell, что означает, "a"прежде чем "A"сортируется. Как и "a"раньше "b". Если это поведение нежелательно, можно захотеть .toLowerCase()каждую букву / строку. то есть. "A".toLowerCase().localeCompare( "a".toLowerCase() )см. developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Adrien Be

2
Потому что сравнение, как я полагаю, часто используется для сортировки / упорядочивания строк. Я прокомментировал здесь давным-давно. ===проверит на равенство, но не будет достаточно хорош для сортировки / упорядочивания строк (см. вопрос, на который я изначально ссылался).
Адриен Бе

Ответы:


1163

Самый простой способ сделать это (если вас не беспокоят специальные символы Юникода) - это вызвать toUpperCase:

var areEqual = string1.toUpperCase() === string2.toUpperCase();

44
Преобразование в верхний или нижний регистр обеспечивает правильное сравнение без учета регистра на всех языках. i18nguy.com/unicode/turkish-i18n.html
Сэмюэл Нефф

57
@sam: я знаю. Вот почему я написал if you're not worried about special Unicode characters.
SLaks

141
Есть ли причина предпочесть toUpperCaseболее toLowerCase?
jpmc26


19
Это действительно лучшее, что может предложить JS?
Кугель

210

РЕДАКТИРОВАТЬ : Этот ответ был первоначально добавлен 9 лет назад. Сегодня вы должны использовать localeCompareс sensitivity: 'accent'опцией:

function ciEquals(a, b) {
    return typeof a === 'string' && typeof b === 'string'
        ? a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0
        : a === b;
}

console.log("'a' = 'a'?", ciEquals('a', 'a'));
console.log("'AaA' = 'aAa'?", ciEquals('AaA', 'aAa'));
console.log("'a' = 'á'?", ciEquals('a', 'á'));
console.log("'a' = 'b'?", ciEquals('a', 'b'));

{ sensitivity: 'accent' }Говорит localeCompare()лечить два варианта одной и той же базовой буквой, то же , если они не имеют разные акценты (как в третьем примере) выше.

В качестве альтернативы вы можете использовать { sensitivity: 'base' }, который рассматривает два символа как эквивалентные, если их базовый символ одинаков (так Aчто будет рассматриваться как эквивалентный á).

Обратите внимание, что третий параметр localeCompareне поддерживается в IE10 или более ранних версиях или в некоторых мобильных браузерах (см. Таблицу совместимости на странице, указанной выше), поэтому, если вам требуется поддержка этих браузеров, вам понадобится некоторый запасной вариант:

function ciEqualsInner(a, b) {
    return a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0;
}

function ciEquals(a, b) {
    if (typeof a !== 'string' || typeof b !== 'string') {
        return a === b;
    }

    //      v--- feature detection
    return ciEqualsInner('A', 'a')
        ? ciEqualsInner(a, b)
        : /*  fallback approach here  */;
}

Оригинальный ответ

Лучший способ выполнить сравнение без учета регистра в JavaScript - это использовать match()метод RegExp с iфлагом.

Поиск без учета регистра

Когда обе сравниваемые строки являются переменными (не константами), то это немного сложнее, потому что вам нужно сгенерировать RegExp из строки, но передача строки в конструктор RegExp может привести к некорректным или неудачным совпадениям, если строка имеет специальное регулярное выражение персонажи в нем.

Если вы заботитесь о интернационализации, не используйте toLowerCase()или, toUpperCase()поскольку она не обеспечивает точного сравнения без учета регистра на всех языках.

http://www.i18nguy.com/unicode/turkish-i18n.html


5
@Quandary, да, это то, что я сказал, нужно было обработать - «вам нужно сгенерировать RegExp из строки, но передача строки в конструктор RegExp может привести к неправильным совпадениям или неудачным совпадениям, если в строке есть специальные символы регулярного выражения»
Сэмюэль Нефф

21
Использование этого является наиболее дорогостоящим решением для сравнения строк без учета регистра. RegExp предназначен для сложного сопоставления с шаблоном, поэтому он должен построить дерево решений для каждого шаблона, а затем выполнить его для входных строк. В то время как это будет работать, это сравнимо с поездкой на реактивном самолете за покупками в следующем квартале. Пожалуйста, не делайте этого.
Агостон Хорват

2
Я мог бы использовать localeCompare (), но он возвращает -1 для 'a'.localeCompare('A')и, как и операции, я ищу сравнения строк без учета регистра.
StingyJack

3
@StingyJack для сравнения без учета регистра с помощью localeCompare, вы должны сделать 'a'.localeCompare (' A ', undefined, {чувствительность:' base '})
Иуда Габриэль Химанго

1
Примечание:localeCompare версия требует, чтобы двигатель JavaScript поддерживает API ECMAScript® Интернационализация , что он не обязан выполнять. Поэтому, прежде чем полагаться на него, вы можете проверить, работает ли он в среде, которую вы используете. Например: const compareInsensitive = "x".localeCompare("X", undefined, {sensitivity: "base"}) === 0 ? (a, b) => a.localeCompare(b, undefined, {sensitivity: "base"}) : (a, b) => a.toLowerCase().localeCompare(b.toLowerCase());или что-то подобное.
TJ Crowder

47

Как сказано в последних комментариях, string::localeCompareподдерживает сравнения без учета регистра (среди других мощных вещей).

Вот простой пример

'xyz'.localeCompare('XyZ', undefined, { sensitivity: 'base' }); // returns 0

И универсальная функция, которую вы могли бы использовать

function equalsIgnoringCase(text, other) {
    return text.localeCompare(other, undefined, { sensitivity: 'base' }) === 0;
}

Обратите внимание, что вместо undefinedвас, вероятно, следует указать конкретную локаль, с которой вы работаете. Это важно, как указано в документах MDN.

на шведском, ä и a - отдельные базовые буквы

Варианты чувствительности

Параметры чувствительности, сведенные в таблицу из MDN

Поддержка браузера

По состоянию на момент публикации, UC Browser для Android и Opera Mini не поддерживает региональные и опционные параметры. Пожалуйста, проверьте https://caniuse.com/#search=localeCompare для получения актуальной информации.


35

С помощью регулярного выражения также мы можем достичь.

(/keyword/i).test(source)

/iдля игнорирования дела. Если в этом нет необходимости, мы можем проигнорировать и проверить на НЕ совпадение с учетом регистра, например

(/keyword/).test(source)

17
Использование такого регулярного выражения будет соответствовать подстрокам! В вашем примере строка keyWORDбудет иметь положительный результат. Но строка this is a keyword yoили keywordsтакже приведет к положительному совпадению. Помните об этом :-)
Элмер

6
Это не отвечает проверке равенства (без учета регистра), как задано в вопросе! Но это проверка содержит ! Не используйте это
S.Serpooshan

4
Конечно, чтобы соответствовать всей строке, регулярное выражение может быть изменено /^keyword$/.test(source), но 1) если keywordоно не является константой, вам нужно это сделать, new RegExp('^' + x + '$').test(source)и 2) прибегнуть к регулярному выражению, чтобы проверить что-то столь же простое, как равенство строк без учета регистра. совсем не очень эффективно.
JHH

28

Помните, что оболочка - это операция, специфичная для локали. В зависимости от сценария вы можете принять это во внимание. Например, если вы сравниваете имена двух людей, вы можете рассмотреть локаль, но если вы сравниваете сгенерированные машиной значения, такие как UUID, то вы можете этого не делать. Вот почему я использую следующую функцию в моей библиотеке утилит (обратите внимание, что проверка типа не включена по соображениям производительности).

function compareStrings (string1, string2, ignoreCase, useLocale) {
    if (ignoreCase) {
        if (useLocale) {
            string1 = string1.toLocaleLowerCase();
            string2 = string2.toLocaleLowerCase();
        }
        else {
            string1 = string1.toLowerCase();
            string2 = string2.toLowerCase();
        }
    }

    return string1 === string2;
}

Есть ли причина, по которой вы используете "!!" выполнить явное логическое преобразование, вместо того чтобы позволить условию if оценивать достоверность значений?
Celos

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

@thekodester в вашей функции есть ошибка. Это compareStrings("", "")даст falseнесмотря на то, что строки равны.
Сергей

@ Сергей делает это trueдля меня. Возможно, это ошибка вашего браузера?
Дженна Слоун

14

Недавно я создал микробиблиотеку, в которой есть помощники для строк без учета регистра: https://github.com/nickuraltsev/ignore-case . (Используется toUpperCaseвнутри.)

var ignoreCase = require('ignore-case');

ignoreCase.equals('FOO', 'Foo'); // => true
ignoreCase.startsWith('foobar', 'FOO'); // => true
ignoreCase.endsWith('foobar', 'BaR'); // => true
ignoreCase.includes('AbCd', 'c'); // => true
ignoreCase.indexOf('AbCd', 'c'); // => 2

12

если вас беспокоит направление неравенства (возможно, вы хотите отсортировать список), вам, скорее всего, нужно выполнить преобразование регистра, и, поскольку в юникоде больше строчных символов, чем в верхнем, toLowerCase, вероятно, является лучшим преобразованием для использования.

function my_strcasecmp( a, b ) 
{
    if((a+'').toLowerCase() > (b+'').toLowerCase()) return 1  
    if((a+'').toLowerCase() < (b+'').toLowerCase()) return -1
    return 0
}

Javascript, кажется, использует локаль "C" для сравнения строк, поэтому результирующий порядок будет уродливым, если строки содержат буквы, отличные от ASCII. с этим мало что можно сделать, не проводя гораздо более детального осмотра строк.


7

Предположим, мы хотим найти строковую переменную needleв строковой переменной haystack. Есть три ошибки:

  1. Интернационализированных приложений следует избегать string.toUpperCaseи string.toLowerCase. Используйте регулярное выражение, которое игнорирует регистр. Например, var needleRegExp = new RegExp(needle, "i");после needleRegExp.test(haystack).
  2. В общем, вы можете не знать значение needle. Будьте осторожны, чтобы в needleрегулярных выражениях не было специальных символов . Побег это, используя needle.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");.
  3. В других случаях, если вы хотите точно соответствовать needleи haystack, просто игнорируя регистр, обязательно добавьте "^"в начале и "$"в конце конструктор вашего регулярного выражения.

Принимая во внимание пункты (1) и (2), примером будет:

var haystack = "A. BAIL. Of. Hay.";
var needle = "bail.";
var needleRegExp = new RegExp(needle.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), "i");
var result = needleRegExp.test(haystack);
if (result) {
    // Your code here
}

Вы держите пари! Все , что вам нужно сделать , это заменить new RegExp(...)часть в строке 3 со следующим: new RegExp("^" + needle.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") + "$", "i");. Это гарантирует отсутствие других символов до или после строки поиска needle.
Крис Шут

4

Существует два способа сравнения без учета регистра:

  1. Преобразуйте строки в верхний регистр, а затем сравните их, используя строгий оператор ( ===). Как строгий оператор относится к операндам, читая материал по адресу: http://www.thesstech.com/javascript/relational-logical-operators
  2. Сопоставление с шаблоном с использованием строковых методов:

Используйте строковый метод поиска для поиска без учета регистра. Читайте о поиске и других строковых методах по адресу: http://www.thesstech.com/pattern-matching-using-string-methods

<!doctype html>
  <html>
    <head>
      <script>

        // 1st way

        var a = "apple";
        var b = "APPLE";  
        if (a.toUpperCase() === b.toUpperCase()) {
          alert("equal");
        }

        //2nd way

        var a = " Null and void";
        document.write(a.search(/null/i)); 

      </script>
    </head>
</html>

4

Здесь много ответов, но мне нравится добавлять решение, основанное на расширении библиотеки String:

String.prototype.equalIgnoreCase = function(str)
{
    return (str != null 
            && typeof str === 'string'
            && this.toUpperCase() === str.toUpperCase());
}

Таким образом, вы можете использовать его так же, как в Java!

Пример:

var a = "hello";
var b = "HeLLo";
var c = "world";

if (a.equalIgnoreCase(b)) {
    document.write("a == b");
}
if (a.equalIgnoreCase(c)) {
    document.write("a == c");
}
if (!b.equalIgnoreCase(c)) {
    document.write("b != c");
}

Выход будет:

"a == b"
"b != c"

String.prototype.equalIgnoreCase = function(str) {
  return (str != null &&
    typeof str === 'string' &&
    this.toUpperCase() === str.toUpperCase());
}


var a = "hello";
var b = "HeLLo";
var c = "world";

if (a.equalIgnoreCase(b)) {
  document.write("a == b");
  document.write("<br>");
}
if (a.equalIgnoreCase(c)) {
  document.write("a == c");
}
if (!b.equalIgnoreCase(c)) {
  document.write("b != c");
}


4

Используйте RegEx для совпадения строк или сравнения.

В JavaScript вы можете использовать match()для сравнения строк, не забудьте поставитьi в RegEx.

Пример:

var matchString = "Test";
if (matchString.match(/test/i)) {
  alert('String matched');
}
else {
 alert('String not matched');
}

1
Убедитесь, что вы в порядке с частичными совпадениями, в противном случае matchString.match(/^test$/i).
хакель

Что вместо "test" в нижнем регистре у вас будет var x = 'test', будет matchString.match(/x/i)работать? Если нет, что будет работать?
Разван Замфир

3

Даже на этот вопрос уже ответили. У меня другой подход к использованию RegExp и match, чтобы игнорировать регистр символов. Пожалуйста, смотрите мою ссылку https://jsfiddle.net/marchdave/7v8bd7dq/27/

$("#btnGuess").click(guessWord);

  function guessWord() {

   var letter = $("#guessLetter").val();
   var word = 'ABC';
   var pattern = RegExp(letter, 'gi'); // pattern: /a/gi

   var result = word.match(pattern);
   alert('Ignore case sensitive:' + result);

  }


3

Если обе строки имеют одну и ту же известную локаль, вы можете использовать Intl.Collatorобъект следующим образом:

function equalIgnoreCase(s1: string, s2: string) {
    return new Intl.Collator("en-US", { sensitivity: "base" }).compare(s1, s2) === 0;
}

Очевидно, что вы можете кэшировать Collatorдля повышения эффективности.

Преимущества этого подхода состоят в том, что он должен быть намного быстрее, чем использование RegExps, и основан на чрезвычайно настраиваемом (см. Описание localesи optionsпараметры конструктора в статье выше) готовом к использованию сборщике.


Другой вариант для чувствительности accent, который сохраняет его без учета регистра, но обрабатывает aи áкак отдельные символы. Так baseили accentможет быть уместно в зависимости от конкретных потребностей.
Мэтью Крамли

2

Я написал расширение. очень тривиально

if (typeof String.prototype.isEqual!= 'function') {
    String.prototype.isEqual = function (str){
        return this.toUpperCase()==str.toUpperCase();
     };
}

1
Что происходит, когда две кодовые базы с разными представлениями о том, как должен работать String # isEqual, пытаются существовать одновременно?
Райан Кавано

3
@KhanSharp Многие люди считают анти-паттерном модифицировать прототип встроенных типов. Вот почему люди могут не голосовать за ваш ответ.
jt000

1
Разве не считается необоснованным предпочтение определений неизвестных методов? Например, как только какой-то браузер решит реализовать String#isEqualили Object#isEqualизначально все ваши страницы будут вести себя по-разному и могут сделать странные вещи, если спецификация не совсем соответствует вашей.
Роберт

1

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

return str1 != null && str2 != null 
    && typeof str1 === 'string' && typeof str2 === 'string'
    && str1.toUpperCase() === str2.toUpperCase();

Приведенный выше фрагмент предполагает, что вы не хотите совпадать, если строка имеет значение null или не определена.

Если вы хотите сопоставить ноль / неопределенное, то:

return (str1 == null && str2 == null)
    || (str1 != null && str2 != null 
        && typeof str1 === 'string' && typeof str2 === 'string'
        && str1.toUpperCase() === str2.toUpperCase());

Если по какой-то причине вы заботитесь о неопределенности против нуля:

return (str1 === undefined && str2 === undefined)
    || (str1 === null && str2 === null)
    || (str1 != null && str2 != null 
        && typeof str1 === 'string' && typeof str2 === 'string'
        && str1.toUpperCase() === str2.toUpperCase());

Или простоstr1 == str2 || ...
SLaks

1

Поскольку ни один ответ явно не предоставил простой фрагмент кода для использования RegExp, вот моя попытка:

function compareInsensitive(str1, str2){ 
  return typeof str1 === 'string' && 
    typeof str2 === 'string' && 
    new RegExp("^" + str1.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + "$", "i").test(str2);
}

У этого есть несколько преимуществ:

  1. Проверяет тип параметра (любой нестроковый параметр, undefinedнапример, может привести к аварийному завершению выражения типа str1.toUpperCase()).
  2. Не страдает от возможных проблем интернационализации.
  3. Избегает RegExpстроки.

Но страдает от недостатка регулярных выражений.
Qwertiy

@Qwertiy Fair Point, добавлено экранирование для каждого stackoverflow.com/a/3561711/67824 .
Охад Шнайдер

0

Это улучшенная версия из этого ответа .

String.equal = function (s1, s2, ignoreCase, useLocale) {
    if (s1 == null || s2 == null)
        return false;

    if (!ignoreCase) {
        if (s1.length !== s2.length)
            return false;

        return s1 === s2;
    }

    if (useLocale) {
        if (useLocale.length)
            return s1.toLocaleLowerCase(useLocale) === s2.toLocaleLowerCase(useLocale)
        else
            return s1.toLocaleLowerCase() === s2.toLocaleLowerCase()
    }
    else {
        if (s1.length !== s2.length)
            return false;

        return s1.toLowerCase() === s2.toLowerCase();
    }
}



Использование и тесты:


0

Преобразуйте оба в нижний (только один раз по соображениям производительности) и сравните их с троичным оператором в одну строку:

function strcasecmp(s1,s2){
    s1=(s1+'').toLowerCase();
    s2=(s2+'').toLowerCase();
    return s1>s2?1:(s1<s2?-1:0);
}

Кто сказал, что С мертв? : D
Сет

0

Если вы знаете, что имеете дело с asciiтекстом, вы можете просто использовать сравнение смещения символов в верхнем и нижнем регистре.

Просто убедитесь, что строка вашей «идеальной» строки (с которой вы хотите сопоставить) строчная:

const CHARS_IN_BETWEEN = 32;
const LAST_UPPERCASE_CHAR = 90; // Z
function strMatchesIgnoreCase(lowercaseMatch, value) {
    let i = 0, matches = lowercaseMatch.length === value.length;
    while (matches && i < lowercaseMatch.length) {
        const a = lowercaseMatch.charCodeAt(i);
        const A = a - CHARS_IN_BETWEEN;
        const b = value.charCodeAt(i);
        const B = b + ((b > LAST_UPPERCASE_CHAR) ? -CHARS_IN_BETWEEN : CHARS_IN_BETWEEN);
        matches = a === b // lowerA === b
            || A === b // upperA == b
            || a === B // lowerA == ~b
            || A === B; // upperA == ~b
        i++;
    }
    return matches;
}

0

Мне нравится этот быстрый вариант стенографии -

export const equalsIgnoreCase = (str1, str2) => {
    return (!str1 && !str2) || (str1 && str2 && str1.toUpperCase() == str2.toUpperCase())
}

Быстрая обработка и делает то, для чего она предназначена.


0

Эта javascript библиотека, кажется, обеспечивает много строковых операций. Это очень удобно использовать

Как установить

npm install --save string

Импортировать

var S = require('string');

Строка сравнения Ignorecase

var isEqual = S('ignoreCase').equalsIgnoreCase('IGNORECASE')
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.