在JavaScript中使用toLowerCase或toUpperCase比较字符串会更好吗?

Jos*_*h R 16 javascript internationalization string-comparison

我正在进行代码审查,我很好奇,如果在尝试比较它们时忽略大小写,那么在JavaScript中将字符串转换为大写或小写更好.

琐碎的例子:

var firstString = "I might be A different CASE";
var secondString = "i might be a different case";
var areStringsEqual = firstString.toLowerCase() === secondString.toLowerCase();
Run Code Online (Sandbox Code Playgroud)

或者我应该这样做:

var firstString = "I might be A different CASE";
var secondString = "i might be a different case";
var areStringsEqual = firstString.toUpperCase() === secondString.toUpperCase();
Run Code Online (Sandbox Code Playgroud)

它似乎"应该"或者只能使用有限的字符集,只有英文字母,所以它比另一个更健壮吗?

作为一个注释,MSDN建议将字符串规范化为大写,但这适用于托管代码(可能是C#和F#,但它们有奇特的StringComparers和基础库):http://msdn.microsoft.com/en-us/library/bb386042. ASPX

Paw*_*yda 20

修改后的答案

我回答这个问题已经有一段时间了.虽然文化问题仍然存在(我认为它们永远不会消失),ECMA-402标准的发展使我的原始答案......过时(或过时了?).

比较本地化字符串的最佳解决方案似乎是使用localeCompare()具有适当语言环境和选项的函数:

var locale = 'en'; // that should be somehow detected and passed on to JS
var firstString = "I might be A different CASE";
var secondString = "i might be a different case";
if (firstString.localeCompare(secondString, locale, {sensitivity: 'accent'}) === 0) {
    // do something when equal
}
Run Code Online (Sandbox Code Playgroud)

这将比较两个字符串不区分大小写,但重音敏感(例如toLocaleUpperCase()toLocaleLowerCase()`将语言环境作为参数传递:? != a).
If this is not sufficient for performance reasons, you may want to use either
or

if (firstString.toLocaleUpperCase(locale) === secondString.toLocaleUpperCase(locale)) {
    // do something when equal
}
Run Code Online (Sandbox Code Playgroud)

理论上应该没有差异.在实践中,细微的实现细节(或在给定的浏览器中缺乏实现)可能会产生不同的结果......

原始答案

我不确定你是否真的打算在国际化(i18n)标签中提出这个问题,但是既然你做了......
可能最意想不到的答案是:两者都没有.

案例转换存在大量问题,如果您想要转换字符大小写而不指示语言(如JavaScript案例中),则不可避免地会导致功能问题.例如:

  1. 有许多自然语言没有大写和小写字符的概念.尝试转换它们没有意义(尽管这会起作用).
  2. 转换字符串有特定于语言的规则.德国尖锐的S字符(ß)必然会被转换成两个大写的S字母(SS).
  3. 土耳其语和阿塞拜疆语(或者阿塞拜疆语,如果你愿意的话)有两个i字符的 "非常奇怪" 概念:无点ı(转换为大写I)和点缀i(转换为大写İ< - 此字体不允许正确呈现,但这是真正不同的字形).
  4. 希腊语有许多"奇怪的"转换规则.一个特殊规则涉及大写字母sigma(Σ),它取决于单词中的一个地方有两个小写的对应物:常规西格玛(σ)和最终西格玛(ς).关于"重音"字符还有其他转换规则,但在转换功能的实现过程中通常会省略它们.
  5. 有些语言有标题大小写的字母,即Lj应转换为类似LJ或不太合适的LJ.同样可以考虑连字.
  6. 最后,有许多兼容性字符可能与您要比较的内容相同,但由完全不同的字符组成.更糟糕的是,像"ae"这样的东西可能相当于德语和芬兰语中的"ä",但相当于丹麦语中的"æ".

我试图说服你,从字面上比较用户输入,而不是转换它真的更好.如果它与用户无关,则可能无关紧要,但案例转换总是需要时间.何必?


小智 6

已经提供了一些其他选项,但如果您必须使用toLowerCase, 或 toUpperCase,我想要一些关于此的实际数据。我提取了因or 失败的两个字节字符的完整列表。然后我运行了这个测试:toLowerCasetoUpperCase

let pairs = [
[0x00E5,0x212B],[0x00C5,0x212B],[0x0399,0x1FBE],[0x03B9,0x1FBE],[0x03B2,0x03D0],
[0x03B5,0x03F5],[0x03B8,0x03D1],[0x03B8,0x03F4],[0x03D1,0x03F4],[0x03B9,0x1FBE],
[0x0345,0x03B9],[0x0345,0x1FBE],[0x03BA,0x03F0],[0x00B5,0x03BC],[0x03C0,0x03D6],
[0x03C1,0x03F1],[0x03C2,0x03C3],[0x03C6,0x03D5],[0x03C9,0x2126],[0x0392,0x03D0],
[0x0395,0x03F5],[0x03D1,0x03F4],[0x0398,0x03D1],[0x0398,0x03F4],[0x0345,0x1FBE],
[0x0345,0x0399],[0x0399,0x1FBE],[0x039A,0x03F0],[0x00B5,0x039C],[0x03A0,0x03D6],
[0x03A1,0x03F1],[0x03A3,0x03C2],[0x03A6,0x03D5],[0x03A9,0x2126],[0x0398,0x03F4],
[0x03B8,0x03F4],[0x03B8,0x03D1],[0x0398,0x03D1],[0x0432,0x1C80],[0x0434,0x1C81],
[0x043E,0x1C82],[0x0441,0x1C83],[0x0442,0x1C84],[0x0442,0x1C85],[0x1C84,0x1C85],
[0x044A,0x1C86],[0x0412,0x1C80],[0x0414,0x1C81],[0x041E,0x1C82],[0x0421,0x1C83],
[0x1C84,0x1C85],[0x0422,0x1C84],[0x0422,0x1C85],[0x042A,0x1C86],[0x0463,0x1C87],
[0x0462,0x1C87]
];

let upper = 0, lower = 0;
for (let pair of pairs) {
   let row = 'U+' + pair[0].toString(16).padStart(4, '0') + ' ';
   row += 'U+' + pair[1].toString(16).padStart(4, '0') + ' pass: ';
   let s = String.fromCodePoint(pair[0]);
   let t = String.fromCodePoint(pair[1]);
   if (s.toUpperCase() == t.toUpperCase()) {
      row += 'toUpperCase ';
      upper++;
   } else {
      row += '            ';
   }
   if (s.toLowerCase() == t.toLowerCase()) {
      row += 'toLowerCase';
      lower++;
   }
   console.log(row);
}
console.log('upper pass: ' + upper + ', lower pass: ' + lower);
Run Code Online (Sandbox Code Playgroud)

有趣的是,其中一对都失败了。但基于此, toUpperCase是最好的选择