Jak porównać ciąg znaków bez rozróżniania wielkości liter?

1056

Jak przeprowadzić porównanie ciągów znaków bez rozróżniania wielkości liter w JavaScript?

Lecieć jak po sznurku
źródło
25
zobacz nowo dodaną .localeCompare()metodę javascript. Obsługiwane tylko przez nowoczesne przeglądarki w momencie pisania (IE11 +). patrz developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Adrien Be
1
patrz także stackoverflow.com/questions/51165/…
Adrien Be
5
@AdrienBe "A".localeCompare( "a" );powraca 1w konsoli Chrome 48.
manuell,
3
@manuell, co oznacza, że "a"pojawia się przed "A"sortowaniem. Tak jak "a"wcześniej "b". Jeśli takie zachowanie nie jest pożądane, można chcieć do .toLowerCase()każdej litery / łańcucha. to znaczy. "A".toLowerCase().localeCompare( "a".toLowerCase() )patrz developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Adrien Be
2
Ponieważ porównanie jest często pojęciem używanym do sortowania / porządkowania ciągów znaków. Skomentowałem tutaj dawno temu. ===sprawdzi równość, ale nie będzie wystarczająca do sortowania / porządkowania ciągów (por. pytanie, z którym pierwotnie się łączyłem).
Adrien Be

Odpowiedzi:

1161

Najprostszym sposobem, aby to zrobić (jeśli nie martwisz się o znaki specjalne Unicode), jest wywołanie toUpperCase:

var areEqual = string1.toUpperCase() === string2.toUpperCase();
SLaks
źródło
44
Konwersja na wielkie lub małe litery zapewnia prawidłowe porównanie bez rozróżniania wielkości liter we wszystkich językach. i18nguy.com/unicode/turkish-i18n.html
Samuel Neff
57
@sam: Wiem. Dlatego napisałem if you're not worried about special Unicode characters.
SLaks,
141
Czy istnieje powód, aby wolą toUpperCaseponad toLowerCase?
jpmc26
19
Czy to naprawdę najlepsza oferta JS?
Kugel
210

EDYCJA : Ta odpowiedź została pierwotnie dodana 9 lat temu. Dzisiaj powinieneś używać localeComparez sensitivity: 'accent'opcją:

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' }Mówi localeCompare()traktować dwa warianty tego samego listu bazowej jako taki sam , chyba że mają różne akcenty (jak w trzecim przykładzie) powyżej.

Alternatywnie możesz użyć { sensitivity: 'base' }, który traktuje dwie postacie jako równoważne, o ile ich podstawowy znak jest taki sam (więc Abyłoby traktowane jako równoważne á).

Pamiętaj, że trzeci parametr parametru localeComparenie jest obsługiwany w przeglądarce IE10 lub nowszej lub w niektórych przeglądarkach mobilnych (zobacz tabelę kompatybilności na powyższej stronie), więc jeśli potrzebujesz obsługi tych przeglądarek, potrzebujesz pewnego rodzaju rezerwy:

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

Oryginalna odpowiedź

Najlepszym sposobem na dokonanie porównania bez rozróżniania wielkości liter w JavaScript jest użycie match()metody RegExp z iflagą.

Wyszukiwanie bez rozróżniania wielkości liter

Gdy oba porównywane ciągi są zmiennymi (a nie stałymi), jest to nieco bardziej skomplikowane, ponieważ musisz wygenerować RegExp z ciągu, ale przekazanie ciągu do konstruktora RegExp może spowodować niepoprawne dopasowanie lub nieudane dopasowanie, jeśli ciąg ma specjalne wyrażenie regularne znaki w nim.

Jeśli dbasz o umiędzynarodowienie nie używać toLowerCase()albo toUpperCase()jak nie dostarcza dokładnych case-niewrażliwe porównań we wszystkich językach.

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

Samuel Neff
źródło
5
@ Pytanie, tak, to, co powiedziałem, musiało zostać obsłużone - „musisz wygenerować RegExp z ciągu, ale przekazanie go do konstruktora RegExp może spowodować niepoprawne dopasowania lub nieudane dopasowania, jeśli ciąg zawiera specjalne znaki wyrażenia regularnego”
Samuel Neff,
21
Korzystanie z tego jest najbardziej kosztownym rozwiązaniem do porównywania ciągów bez rozróżniania wielkości liter. RegExp jest przeznaczony do skomplikowanego dopasowywania wzorców, dlatego musi zbudować drzewo decyzyjne dla każdego wzorca, a następnie wykonać je na ciągach wejściowych. Choć zadziałałoby, jest to porównywalne z odrzutowym samolotem na zakupy w następnym bloku. tl; dr: proszę nie rób tego.
Agoston Horvath,
2
Mógłbym użyć localeCompare (), ale zwraca -1 dla 'a'.localeCompare('A')i jak operacja szukam ciąg znaków bez rozróżniania wielkości liter.
StingyJack
3
@StingyJack, aby porównywać bez rozróżniania wielkości liter za pomocą localeCompare, powinieneś zrobić 'a'.localeCompare (' A ', niezdefiniowany, {wrażliwość:' baza '})
Judah Gabriel Himango
1
Uwaga:localeCompare wersja wymaga, że silnik JavaScript obsługuje API ECMAScript® Internacjonalizacja , która jest nie wymagany robić. Dlatego zanim zaczniesz na nim polegać, możesz sprawdzić, czy działa w używanym środowisku. Na przykład: const compareInsensitive = "x".localeCompare("X", undefined, {sensitivity: "base"}) === 0 ? (a, b) => a.localeCompare(b, undefined, {sensitivity: "base"}) : (a, b) => a.toLowerCase().localeCompare(b.toLowerCase());lub niektóre takie.
TJ Crowder,
46

Jak powiedziano w ostatnich komentarzach, string::localeCompareobsługuje porównania bez rozróżniania wielkości liter (między innymi ważnymi rzeczami).

Oto prosty przykład

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

I ogólna funkcja, której możesz użyć

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

Zauważ, że zamiast tego undefinedpowinieneś prawdopodobnie wprowadzić określone ustawienia regionalne, z którymi pracujesz. Jest to ważne, jak wskazano w dokumentach MDN

w języku szwedzkim ä i a to osobne litery podstawowe

Opcje czułości

Opcje czułości zestawione w tabeli z MDN

Obsługa przeglądarki

W chwili publikacji UC Browser dla Androida i Opera Mini nie obsługują ustawień regionalnych i parametrów opcji . Sprawdź https://caniuse.com/#search=localeCompare , aby uzyskać aktualne informacje.

Jay Wick
źródło
35

Z pomocą wyrażeń regularnych możemy również osiągnąć.

(/keyword/i).test(source)

/ijest dla ignorowania wielkości liter. Jeśli nie jest to konieczne, możemy zignorować i przetestować pod kątem NIE rozróżniania wielkich i małych liter

(/keyword/).test(source)
SP007
źródło
17
Użycie takiego wyrażenia regularnego dopasuje podciągi! W twoim przykładzie ciąg keyWORDzostanie wycięty, co spowoduje pozytywne dopasowanie. Ale ciąg this is a keyword yolub keywordsspowoduje również pozytywne dopasowanie. Miej to na uwadze :-)
Elmer,
6
To nie odpowiada na sprawdzenie równości (bez rozróżniania wielkości liter), jak zadano w pytaniu! Ale to jest czek zawierający ! Nie używaj go
S.Serpooshan,
4
Oczywiście, aby dopasować cały ciąg, wyrażenie regularne można zmienić na /^keyword$/.test(source), ale 1) jeśli keywordnie jest stałe, należy to zrobić new RegExp('^' + x + '$').test(source)i 2) odwołać się do wyrażenia regularnego w celu przetestowania czegoś tak prostego, jak równość ciągów bez rozróżniania wielkości liter wcale nie bardzo wydajny.
JHH
28

Pamiętaj, że obudowa jest operacją zależną od ustawień regionalnych. W zależności od scenariusza możesz wziąć to pod uwagę. Na przykład, jeśli porównujesz nazwiska dwóch osób, możesz rozważyć ustawienia regionalne, ale jeśli porównujesz wartości generowane maszynowo, takie jak UUID, możesz tego nie zrobić. Dlatego używam następującej funkcji w mojej bibliotece utils (zauważ, że sprawdzanie typu nie jest uwzględnione ze względu na wydajność).

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;
}
Shital Shah
źródło
Czy istnieje powód, dla którego używasz „!!” wykonać jawną konwersję boolowską, zamiast pozwolić klauzuli if ocenić prawdziwość wartości?
Celos
To nie jest wymagane. Wydaje mi się, że miałem to z innej wersji bardziej skomplikowanego kodu. Zaktualizowałem odpowiedź.
Shital Shah
@ thekodester Twoja funkcja ma błąd. To compareStrings("", "")da, falsepomimo że łańcuchy są równe.
Siergiej
@Sergey Robi to, co truedla mnie powraca . Być może jest to błąd przeglądarki?
Jenna Sloan
14

Niedawno utworzyłem mikro bibliotekę, która zapewnia pomocniki bez rozróżniania wielkości liter: https://github.com/nickuraltsev/ignore-case . (Wykorzystuje toUpperCasewewnętrznie.)

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
Nick Uraltsev
źródło
12

jeśli martwisz się kierunkiem nierówności (być może chcesz posortować listę), prawie musisz przeprowadzić konwersję wielkości liter, a ponieważ w kodzie Unicode jest więcej małych liter niż wielkich liter, to prawdopodobnie najlepsza konwersja do użycia.

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

Wygląda na to, że JavaScript używa ustawień regionalnych „C” do porównywania ciągów, więc wynikowe uporządkowanie będzie brzydkie, jeśli ciągi zawierają inne niż litery ASCII. niewiele można z tym zrobić bez dokładniejszej kontroli napisów.

Jasen
źródło
7

Załóżmy, że chcemy znaleźć zmienną needlełańcuchową w zmiennej łańcuchowej haystack. Istnieją trzy gotcha:

  1. Międzynarodowe aplikacje powinny unikać string.toUpperCasei string.toLowerCase. Zamiast tego użyj wyrażenia regularnego, które ignoruje wielkość liter. Na przykład, var needleRegExp = new RegExp(needle, "i");po którym następujeneedleRegExp.test(haystack) .
  2. Ogólnie rzecz biorąc, możesz nie znać wartości needle. Uważaj, aby needlenie zawierało żadnych znaków specjalnych wyrażeń regularnych . Ucieczka za pomocąneedle.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); .
  3. W innych przypadkach, jeśli chcesz dokładnie dopasować needlei haystack, po prostu ignorując "^"wielkość liter, upewnij się, że dodajesz na początku i "$"na końcu konstruktora wyrażeń regularnych.

Biorąc pod uwagę punkty (1) i (2), przykładem może być:

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
}
Chris Chute
źródło
Obstawiasz! Wszystko, co musisz zrobić, to zastąpić new RegExp(...)część w wierszu 3 następującym tekstem new RegExp("^" + needle.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") + "$", "i");:. Dzięki temu nie będzie żadnych innych znaków przed ani za ciągiem wyszukiwania needle.
Chris Chute
4

Istnieją dwa sposoby porównywania bez rozróżniania wielkości liter:

  1. Konwertuj ciągi znaków na wielkie litery, a następnie porównuj je za pomocą ścisłego operatora ( ===). Jak surowy operator traktuje operandy czytać rzeczy na: http://www.thesstech.com/javascript/relational-logical-operators
  2. Dopasowywanie wzorców za pomocą metod łańcuchowych:

Użyj metody „szukaj” w celu wyszukiwania bez rozróżniania wielkości liter. Przeczytaj o wyszukiwaniu i innych metodach ciągów na: 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>
Sohail Arif
źródło
4

Tutaj jest wiele odpowiedzi, ale lubię dodawać rozwiązanie oparte na rozszerzeniu lib String:

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

W ten sposób możesz po prostu używać go tak jak w Javie!

Przykład:

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

Dane wyjściowe będą:

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

Nebulosar
źródło
4

Użyj RegEx do dopasowania ciągu lub porównania.

W JavaScript możesz używać match()do porównywania ciągów, nie zapomnij wstawići RegEx.

Przykład:

var matchString = "Test";
if (matchString.match(/test/i)) {
  alert('String matched');
}
else {
 alert('String not matched');
}
Om Sharma
źródło
1
Upewnij się, że nie masz nic przeciwko częściowym dopasowaniom, w przeciwnym razie matchString.match(/^test$/i).
hackel
Czym byłby zamiast „test” małych liter var x = „test”, działałby matchString.match(/x/i)? Jeśli nie, co by zadziałało?
Razvan Zamfir
3
str = 'Lol', str2 = 'lOl', regex = new RegExp('^' + str + '$', 'i');
if (regex.test(str)) {
    console.log("true");
}
Parth Raval
źródło
3

Jeśli oba ciągi znaków mają takie same znane ustawienia narodowe, możesz chcieć użyć Intl.Collatorobiektu takiego:

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

Oczywiście możesz chcieć buforować Collatordla lepszej wydajności.

Zaletą tego podejścia jest to, że powinno być znacznie szybsze niż użycie RegExps i opiera się na niezwykle konfigurowalnym (patrz opis localesi optionsparametry konstruktora w powyższym artykule) zestawie gotowych do użycia kolektorów.

Aleksander Abakumow
źródło
Inną opcją dla czułości jest to accent, że nie rozróżnia wielkości liter, ale traktuje ai ájako osobne znaki. Tak więc baselub accentoba mogą być odpowiednie w zależności od dokładnych potrzeb.
Matthew Crumley,
2

Napisałem rozszerzenie. bardzo trywialne

if (typeof String.prototype.isEqual!= 'function') {
    String.prototype.isEqual = function (str){
        return this.toUpperCase()==str.toUpperCase();
     };
}
Jhankar Mahbub
źródło
1
Co dzieje się w dwóch bazach kodowych z różnymi pomysłami na to, jak String # isEqual powinien działać w tym samym czasie?
Ryan Cavanaugh
3
@KhanSharp Wiele osób uważa, że ​​modyfikacja prototypu wbudowanych typów jest anty-wzorem. To dlatego ludzie mogą głosować na twoją odpowiedź.
jt000
1
Czy nie jest źle traktowane preferowanie nieznanych definicji metod? Na przykład, gdy tylko jakaś przeglądarka zdecyduje się zaimplementować String#isEquallub Object#isEqualnatywnie wszystkie twoje strony zachowują się inaczej i mogą robić dziwne rzeczy, jeśli specyfikacja nie pasuje dokładnie do twojej.
Robert
2

Nawet na to pytanie już udzielono odpowiedzi. Mam inne podejście do używania RegExp i dopasowywania, aby ignorować wielkość liter. Proszę zobaczyć mój link 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);

  }
David S. Lee
źródło
1

Co powiesz na NIE zgłaszanie wyjątków i NIE używanie powolnego wyrażenia regularnego?

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

Powyższy fragment zakłada, że ​​nie chcesz dopasowywać, jeśli dowolny ciąg ma wartość NULL lub jest niezdefiniowany.

Jeśli chcesz dopasować wartość null / undefined, to:

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

Jeśli z jakiegoś powodu zależy Ci na niezdefiniowanym kontra zerowym:

return (str1 === undefined && str2 === undefined)
    || (str1 === null && str2 === null)
    || (str1 != null && str2 != null 
        && typeof str1 === 'string' && typeof str2 === 'string'
        && str1.toUpperCase() === str2.toUpperCase());
Ben Wilde
źródło
Lub po prostustr1 == str2 || ...
SLaks,
1

Ponieważ żadna odpowiedź nie zawierała prostego fragmentu kodu do użycia RegExp, oto moja próba:

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

Ma kilka zalet:

  1. Weryfikuje typ parametru (dowolny parametr nieciągowy, taki jak undefinedna przykład, spowodowałby awarię wyrażenia podobnegostr1.toUpperCase() ).
  2. Nie ma problemów z internacjonalizacją.
  3. Ucieka z RegExpciągu.
Ohad Schneider
źródło
Ale cierpi na brak ucieczki wyrażeń regularnych.
Qwertiy
@Qwertiy punkt sprawiedliwe, dodaje ucieczce za stackoverflow.com/a/3561711/67824 .
Ohad Schneider
0

Jest to ulepszona wersja od tej odpowiedzi .

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



Zastosowania i testy:

Siergiej
źródło
0

Konwertuj oba na niższe (tylko raz ze względu na wydajność) i porównaj je z operatorem trójskładnikowym w jednym wierszu:

function strcasecmp(s1,s2){
    s1=(s1+'').toLowerCase();
    s2=(s2+'').toLowerCase();
    return s1>s2?1:(s1<s2?-1:0);
}
Luca C.
źródło
Kto powiedział, że C nie żyje? : D
Seth
0

Jeśli wiesz, że masz do czynienia z asciitekstem, możesz po prostu użyć porównania przesunięcia wielkich i małych liter.

Upewnij się tylko, że ciąg „idealny” (ten, z którym chcesz się dopasować) jest pisany małymi literami:

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;
}
matsko
źródło
0

Podoba mi się ta szybka stenografia -

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

Szybkie przetwarzanie i robi to, co jest przeznaczone.

Neetesh Dadwariya
źródło
0

To javascript biblioteka wydaje się zapewniać wiele operacji na łańcuchach znaków. Jest bardzo wygodny w użyciu

Jak zainstalować

npm install --save string

Import

var S = require('string');

Ignoruj ​​Porównaj ciąg

var isEqual = S('ignoreCase').equalsIgnoreCase('IGNORECASE')
akash
źródło