Як зробити порівняння рядків нечутливих до регістру?


1056

Як виконати порівняння рядків нечутливих до регістру в JavaScript?


25
переглянути нещодавно доданий .localeCompare()метод javascript. Підтримується лише сучасними браузерами під час написання (IE11 +). дивіться developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Adrien Be


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
Тому що порівняння часто використовується терміном, який використовується для сортування / упорядкування рядків. Я коментував тут давно зараз. ===буде перевіряти рівність, але не буде достатньо хорошим для сортування / упорядкування рядків (див. питання, з яким я спочатку пов'язувався).
Adrien Be

Відповіді:


1162

Найпростіший спосіб зробити це (якщо вас не турбують спеціальні символи Unicode) - зателефонувати 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.
СЛАкс

141
Чи є причина віддавати перевагу toUpperCaseнад toLowerCase?
jpmc26


19
Це справді найкраще, що може запропонувати JS?
Кугель

210

EDIT : Ця відповідь спочатку була додана 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 призначений для складного узгодження шаблону, як такого, йому потрібно побудувати дерево рішень для кожного шаблону, а потім виконувати його проти вхідних рядків. Хоча це буде працювати, це порівнянно з тим, як взяти реактивний літак, щоб піти по магазинах на наступний блок. tl; dr: будь ласка, не робіть цього.
Агостон Хорват

2
Я міг би використати localeCompare (), але його повернення -1 для 'a'.localeCompare('A')і, як оп, я шукаю порівняння рядків нечутливих до випадку.
StingyJack

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

1
Примітка . localeCompareВерсія вимагає, щоб механізм JavaScript підтримував інтерфейс API ECMAScript® Internationalization , чого не потрібно робити. Тому перш ніж покластися на нього, ви можете перевірити, чи працює він у середовищі, яке ви використовуєте. Наприклад: 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

46

Як було сказано в останніх коментарях, 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=localeПорівняйте актуальну інформацію.


35

За допомогою регулярного вираження також ми можемо досягти.

(/keyword/i).test(source)

/iпризначений для ігнорування. Якщо немає необхідності, ми можемо проігнорувати і протестувати НЕ чутливі до регістру відповідність

(/keyword/).test(source)

17
Використання такої регулярної виразки буде відповідати підрядкам! У вашому прикладі рядок keyWORDнеприпустимо призведе до позитивної відповідності. Але рядок this is a keyword yoабо keywordsтакож призведе до позитивного матчу. Будьте в курсі цього :-)
Elmer

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, щоб оцінити правдивість значень?
Целос

Це не потрібно. Я здогадуюсь, що це було у моєї іншої версії більш складного коду. Я оновив відповідь.
Shital Shah

@thekodester у вашій функції є помилка. Це compareStrings("", "")дасть falseпопри те, що струни рівні.
Сергій

@Sergey Робити, що повертається 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

якщо вас турбує напрямок нерівності (можливо, ви хочете сортувати список), вам досить-таки доведеться перетворити регістр, і оскільки в unicode є більше малих символів, ніж у верхньому регістрі 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.
Chris Chute

4

Існує два способи порівняння нечутливих випадків:

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

Використовуйте рядовий метод "search" для нечутливого пошуку регістру. Про пошук та інші рядкові методи читайте на веб-сайті: 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()для порівняння рядків, не забудьте ввести iRegEx.

Приклад:

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

1
Переконайтеся , що ви все в порядку з частковими збігами, в іншому випадку matchString.match(/^test$/i).
hackel

Що замість малого "тесту" у вас var x = 'test', буде matchString.match(/x/i)працювати? Якщо ні, то що б спрацювало?
Разван Замфір


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основному всі ваші сторінки ведуть себе по-різному і можуть зробити дивні речі, якщо специфікація не відповідає точно вашій.
Роберт

2

Навіть на це питання вже відповіли. У мене інший підхід використовувати 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);

  }

1

Як щодо НЕ кидання винятків і НЕ використання повільного регулярного вираження?

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

Вищенаведений фрагмент передбачає, що ви не хочете відповідати, якщо будь-яка рядок є нульовою або невизначеною.

Якщо ви хочете відповідати null / undefined, тоді:

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 справедливий момент, додано втечу за 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');

Ігнорувати порівняння рядка

var isEqual = S('ignoreCase').equalsIgnoreCase('IGNORECASE')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.