【问题标题】:How to do case insensitive string comparison?如何进行不区分大小写的字符串比较?
【发布时间】:2011-01-09 14:00:06
【问题描述】:

如何在 JavaScript 中执行不区分大小写的字符串比较?

【问题讨论】:

  • 查看新添加的.localeCompare() javascript方法。在撰写本文时仅受现代浏览器支持 (IE11+)。见developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • @AdrienBe "A".localeCompare( "a" ); 在 Chrome 48 控制台中返回 1
  • @manuell 这意味着在排序时"a""A" 之前。就像"a""b" 之前一样。如果不需要这种行为,可能需要.toLowerCase() 每个字母/字符串。 IE。 "A".toLowerCase().localeCompare( "a".toLowerCase() )developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
  • 因为我想比较通常是用于排序/排序字符串的术语。我很久以前在这里评论过。 === 将检查是否相等,但不足以对字符串进行排序/排序(参见我最初链接到的问题)。

标签: javascript string


【解决方案1】:

最简单的方法是调用toUpperCase

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

【讨论】:

  • 转换为大写或小写确实可以在所有语言中提供正确的不区分大小写的比较。 i18nguy.com/unicode/turkish-i18n.html
  • @sam:我知道。这就是我写if you're not worried about special Unicode characters的原因。
  • 有理由更喜欢toUpperCase 而不是toLowerCase
  • @Kugel 这个答案已经 9 岁了。从那时起就有了新功能,因此截至您发表评论之日,不,这不是 JS 所能提供的最好的。请参阅下面关于localeCompare 的答案,该答案最近更新(但仍是五年前)。我不知道在过去五年中有什么变化会使localeCompare 不再是最佳答案。
【解决方案2】:

编辑:这个答案最初是 9 年前添加的。今天你应该使用localeComparesensitivity: '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 中进行不区分大小写比较的最佳方法是使用带有 i 标志的 RegExp match() 方法。

Case-insensitive search

当被比较的两个字符串都是变量(不是常量)时,它会稍微复杂一些,因为您需要从字符串生成 RegExp,但是将字符串传递给 RegExp 构造函数可能会导致不正确的匹配或匹配失败的字符串里面有特殊的正则表达式字符。

如果您关心国际化,请不要使用 toLowerCase()toUpperCase(),因为它不能在所有语言中提供准确的不区分大小写的比较。

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

【讨论】:

  • @Quandary,是的,这就是我所说的必须处理的内容——“您需要从字符串生成 RegExp,但将字符串传递给 RegExp 构造函数可能会导致匹配错误或匹配失败,如果字符串中有特殊的正则表达式字符”
  • 使用这是不区分大小写的字符串比较最昂贵的解决方案。 RegExp 用于复杂的模式匹配,因此,它需要为每个模式构建一个决策树,然后针对输入字符串执行该决策树。虽然它会起作用,但它与乘坐喷气式飞机去下一个街区购物相当。 tl;博士:请不要这样做。
  • 我可以使用 localeCompare(),但它为 'a'.localeCompare('A') 返回 -1,就像我正在寻找不区分大小写的字符串比较的操作一样。
  • @StingyJack 使用 localeCompare 做不区分大小写的比较,你应该做 'a'.localeCompare('A', undefined, {sensitive: 'base' })
  • 注意:localeCompare 版本要求 JavaScript 引擎支持 ECMAScript® Internationalization API,而这不需要。因此,在依赖它之前,您可能需要检查它是否在您正在使用的环境中工作。例如:const compareInsensitive = "x".localeCompare("X", undefined, {sensitivity: "base"}) === 0 ? (a, b) => a.localeCompare(b, undefined, {sensitivity: "base"}) : (a, b) => a.toLowerCase().localeCompare(b.toLowerCase()); 或类似的。
【解决方案3】:

正如最近的 cmets 所说,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 是独立的基本字母

灵敏度选项

浏览器支持

截至发稿时,适用于 Android 和 Opera Mini 的 UC 浏览器支持 localeoptions 参数。请查看https://caniuse.com/#search=localeCompare 获取最新信息。

【讨论】:

  • 我很确定浏览器不会定期删除对旧版本功能的支持,因此此处列出的浏览器支持不正确或至少不完整(根据您的 caniuse关联)。 IE locale 和 options 参数,并且很可能在发布此答案时不支持。对于新读者来说,这可能比列出的那些基本上不支持任何现代功能的晦涩浏览器有用得多。
【解决方案4】:

更新:

根据 cmets,之前的答案会检查 source contains keyword,以使其相等性检查添加了 ^$

(/^keyword$/i).test(source)

借助正则表达式也可以实现。

(/keyword/i).test(source)

/i 用于忽略大小写。如果没有必要,我们可以忽略并测试不区分大小写的匹配项,例如

(/keyword/).test(source)

【讨论】:

  • 使用这样的正则表达式将匹配子字符串!在您的示例中,字符串 keyWORD 将导致肯定匹配。但是字符串this is a keyword yokeywords 也会导致肯定匹配。请注意:-)
  • 这不回答问题中提出的 Equality 检查(不区分大小写)!但是,这是一个包含检查!不要使用它
  • 当然,为了匹配整个字符串,正则表达式可以更改为/^keyword$/.test(source),但是1)如果keyword不是一个常量,你需要做new RegExp('^' + x + '$').test(source)和2 ) 使用正则表达式来测试一些简单的东西,比如不区分大小写的字符串相等性根本不是很有效。
【解决方案5】:

请记住,大小写是特定于语言环境的操作。根据情况,您可能需要考虑到这一点。例如,如果您要比较两个人的姓名,您可能需要考虑语言环境,但如果您要比较机器生成的值(例如 UUID),则可能不需要。这就是为什么我在我的 utils 库中使用以下函数的原因(请注意,出于性能原因,不包括类型检查)。

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 子句评估值的真实性?
  • 这不是必需的。我想我是从其他版本的更复杂的代码中得到的。我已经更新了答案。
  • @thekodester 您的函数有错误。这个compareStrings("", "") 将给出false,尽管字符串是相等的。
  • @Sergey 这样做会为我返回true。也许这是您的浏览器的错误?
  • 语言环境绝对是一个需要考虑的重要因素。 +1
【解决方案6】:

如果您关心不等式的方向(也许您想对列表进行排序) 您几乎必须进行大小写转换,并且由于 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】:

    我最近创建了一个提供不区分大小写的字符串助手的微型库: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
    

    【讨论】:

      【解决方案8】:

      假设我们要在字符串变量haystack中找到字符串变量needle。有三个陷阱:

      1. 国际化应用程序应避免使用string.toUpperCasestring.toLowerCase。改用忽略大小写的正则表达式。例如,var needleRegExp = new RegExp(needle, "i"); 后跟 needleRegExp.test(haystack)
      2. 一般来说,您可能不知道needle 的值。注意needle 不包含任何正则表达式special characters。使用needle.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&amp;"); 转义这些。
      3. 在其他情况下,如果您想精确匹配needlehaystack,只需忽略大小写,请确保在正则表达式构造函数的开头添加"^",在末尾添加"$"。李>

      考虑到第(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
      }
      

      【讨论】:

      • 你打赌!您需要做的就是将第 3 行中的 new RegExp(...) 部分替换为以下内容:new RegExp("^" + needle.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&amp;") + "$", "i");。这可确保在您的搜索字符串 needle 之前或之后没有其他字符。
      【解决方案9】:

      这里有很多答案,但我想添加一个基于扩展字符串库的解决方案:

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

      【讨论】:

        【解决方案10】:

        使用 RegEx 进行字符串匹配或比较。

        在 JavaScript 中,您可以使用match() 进行字符串比较, 不要忘记在正则表达式中输入i

        例子:

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

        【讨论】:

        • 确保您可以接受部分匹配,否则matchString.match(/^test$/i)
        • 你有 var x = 'test' 而不是小写的“test”,matchString.match(/x/i) 可以工作吗?如果没有,什么会起作用?
        • @RazvanZamfir , 如果是动态模式,可以使用 RegExp Object Ex: var x = new RegExp(/test/, "gi"); matchString.match(x);
        • 或者const x_regex = new RegExp(`^${x}$`, "gi");,虽然那很乱。
        【解决方案11】:

        不区分大小写比较有两种方式:

        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>
        

        【讨论】:

          【解决方案12】:

          如果两个字符串属于相同的已知语言环境,您可能希望像这样使用Intl.Collator 对象:

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

          显然,您可能希望缓存 Collator 以提高效率。

          这种方法的优点是它应该比使用 RegExps 快得多,并且基于一个高度可定制(参见上面文章中localesoptions 构造函数参数的描述)的即用型集合整理者。

          【讨论】:

          • 另一个敏感选项是accent,它不区分大小写,但将aá 视为单独的字符。所以baseaccent 都可能是合适的,具体取决于具体需求。
          【解决方案13】:

          连这个问题都已经回答了。我有一种不同的方法来使用 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);
          
            }
          

          【讨论】:

            【解决方案14】:
            str = 'Lol', str2 = 'lOl', regex = new RegExp('^' + str + '$', 'i');
            if (regex.test(str)) {
                console.log("true");
            }
            

            【讨论】:

              【解决方案15】:

              我喜欢这种快速的速记变化 -

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

              处理速度快,并按预期行事。

              【讨论】:

                【解决方案16】:

                我写了一个扩展。很琐碎

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

                【讨论】:

                • 两个对 String#isEqual 应该如何工作有不同想法的代码库试图同时存在会发生什么?
                • @KhanSharp 很多人认为修改内置类型的原型是一种反模式。这就是为什么人们可能会对您的答案投反对票。
                • 偏爱未知的方法定义不是考虑不周吗?例如,一旦某些浏览器决定原生实现String#isEqualObject#isEqual,您的所有页面的行为都会有所不同,如果规范与您的规范不完全匹配,则可能会出现奇怪的事情。
                【解决方案17】:

                不抛出异常和不使用慢正则表达式怎么样?

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

                上面的 sn-p 假设如果字符串为空或未定义,您不想匹配。

                如果你想匹配 null/undefined,那么:

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

                如果出于某种原因您关心 undefined vs null:

                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 || ...
                【解决方案18】:

                由于没有答案明确提供了使用RegExp 的简单代码sn-p,这是我的尝试:

                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 字符串。

                【讨论】:

                【解决方案19】:

                如果您知道您正在处理 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;
                }
                

                【讨论】:

                  【解决方案20】:

                  为了更好的浏览器兼容性,您可以依赖正则表达式。这将适用于过去 20 年发布的所有网络浏览器:

                  String.prototype.equalsci = function(s) {
                      var regexp = RegExp("^"+this.replace(/[.\\+*?\[\^\]$(){}=!<>|:-]/g, "\\$&")+"$", "i");
                      return regexp.test(s);
                  }
                  
                  "PERSON@Ü.EXAMPLE.COM".equalsci("person@ü.example.com")// returns true
                  

                  这与此处找到的其他答案不同,因为它考虑到并非所有用户都使用现代网络浏览器。

                  注意:如果您需要支持土耳其语等特殊情况,则需要使用 localeCompare,因为 i 和 I 在土耳其语中不是同一个字母。

                  "I".localeCompare("i", undefined, { sensitivity:"accent"})===0// returns true
                  "I".localeCompare("i", "tr", { sensitivity:"accent"})===0// returns false
                  

                  【讨论】:

                  • 最好使用 Object.defineProperty 来避免 equalsci 方法被枚举。
                  【解决方案21】:

                  将两者都转换为较低的字符串(出于性能原因仅一次)并将它们与内联三元运算符进行比较:

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

                  【讨论】:

                  • 谁说 C 已经死了? :D
                  【解决方案22】:

                  这是this answer改进版

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



                  用法和测试:

                  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();
                      }
                  }
                  
                  // If you don't mind extending the prototype.
                  String.prototype.equal = function(string2, ignoreCase, useLocale) {
                    return String.equal(this.valueOf(), string2, ignoreCase, useLocale);
                  }
                  
                  // ------------------ TESTS ----------------------
                  console.log("Tests...");
                  
                  console.log('Case sensitive 1');
                  var result = "Abc123".equal("Abc123");
                  console.assert(result === true);
                  
                  console.log('Case sensitive 2');
                  result = "aBC123".equal("Abc123");
                  console.assert(result === false);
                  
                  console.log('Ignore case');
                  result = "AbC123".equal("aBc123", true);
                  console.assert(result === true);
                  
                  console.log('Ignore case + Current locale');
                  result = "AbC123".equal("aBc123", true);
                  console.assert(result === true);
                  
                  console.log('Turkish test 1 (ignore case, en-US)');
                  result = "IiiI".equal("ıiİI", true, "en-US");
                  console.assert(result === false);
                  
                  console.log('Turkish test 2 (ignore case, tr-TR)');
                  result = "IiiI".equal("ıiİI", true, "tr-TR");
                  console.assert(result === true);
                  
                  console.log('Turkish test 3 (case sensitive, tr-TR)');
                  result = "IiiI".equal("ıiİI", false, "tr-TR");
                  console.assert(result === false);
                  
                  console.log('null-test-1');
                  result = "AAA".equal(null);
                  console.assert(result === false);
                  
                  console.log('null-test-2');
                  result = String.equal(null, "BBB");
                  console.assert(result === false);
                  
                  console.log('null-test-3');
                  result = String.equal(null, null);
                  console.assert(result === false);

                  【讨论】:

                    【解决方案23】:

                    我们也可以使用 ASCII 来做到这一点:

                    function toLower(a){
                    
                        let c = "";
                    
                        
                        for(let i = 0;i<a.length;i++){
                    
                            
                            let f = a.charCodeAt(i);
                            if(f < 95){
                    
                                c += String.fromCharCode(f+32);
                            }
                            else{
                    
                                c += a[i];
                            }
                        }
                    
                        return c;
                    }
                    function compareIt(a,b){
                    
                    
                        return toLower(a)==toLower(b);
                    
                    
                    }
                    console.log(compareIt("An ExamPlE" , "an example"));
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2015-08-22
                      • 1970-01-01
                      相关资源
                      最近更新 更多