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

问题描述 投票:0回答:4

我正在进行代码审查,我很好奇在尝试比较字符串并忽略大小写时,是否最好在 JavaScript 中将字符串转换为大写或小写。

小例子:

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

或者我应该这样做:

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

似乎“应该”或可以使用有限的字符集(例如仅英文字母),那么一个比另一个更强大吗?

请注意,MSDN 建议将字符串规范化为大写,但那是针对托管代码(大概是 C# 和 F#,但它们有精美的

StringComparers
和基础库):

http://msdn.microsoft.com/en-us/library/bb386042.aspx

javascript internationalization string-comparison
4个回答
32
投票

修改答案

我回答这个问题已经有一段时间了。虽然文化问题仍然存在(而且我认为它们永远不会消失),但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
}

这将比较两个不区分大小写但区分重音的字符串(例如

ą != a).
If this is not sufficient for performance reasons, you may want to use either
toLocaleUpperCase()
or
toLocaleLowerCase()` 将语言环境作为参数传递:

if (firstString.toLocaleUpperCase(locale) === secondString.toLocaleUpperCase(locale)) {
    // do something when equal
}

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

原答案

我不确定你是否真的想在 Internationalization (i18n) 标签中问这个问题,但既然你这样做了......
也许最出乎意料的答案是:都不是

大小写转换存在大量问题,如果您想在不指示语言的情况下转换字符大小写(例如 JavaScript 大小写),这不可避免地会导致功能问题。例如:

  1. 有许多自然语言没有大小写字符的概念。尝试转换它们没有意义(尽管这会起作用)。
  2. 转换字符串有特定于语言的规则。德语锐 S 字符 (ß) 必然会转换为两个大写 S 字母 (SS)。
  3. 土耳其语和阿塞拜疆语(或者阿塞拜疆语,如果您愿意)有“非常奇怪”两个 i 字符的概念:无点 ı(转换为大写 I)和点式 i(转换为大写 ı <- this font does not allow for correct presentation, but this is really different glyph).
  4. 希腊语有很多“奇怪”的转换规则。一个特定的规则涉及大写字母 sigma (Σ),根据单词中的位置,它有两个对应的小写字母:常规 sigma (σ) 和最终 sigma (ς)。关于“重音”字符还有其他转换规则,但在转换函数的实现过程中通常会被省略。
  5. 某些语言有首字母大写字母,即 Lj 应该转换为 LJ 或不太合适的 LJ 之类的东西。 连字也是如此。
  6. 最后,有许多兼容字符可能与您要比较的含义相同,但由完全不同的字符组成。更糟糕的是,像“ae”这样的东西可能相当于德语和芬兰语中的“ä”,但相当于丹麦语中的“æ”。

我试图说服您,按字面比较用户输入确实比转换它更好。如果它与用户无关,可能并不重要,但大小写转换总是需要时间。何苦呢?


6
投票

已经提供了一些其他选项,但如果您必须使用

toLowerCase
,或者
toUpperCase
,我想要一些关于这方面的实际数据。我拉了完整列表 失败的两个字节字符的
toLowerCase
toUpperCase
。我然后 进行此测试:

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

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


0
投票

如果您不想使用基于区域设置的解决方案,您可以简单地执行以下操作:

const areStringsEqual = (a, b) => 
    a.toLowerCase().toUpperCase() === b.toLowerCase().toUpperCase()

(注意:相反的

a.toUpperCase().toLowerCase()
无法工作,因为一种奇怪的现象:小写
 会导致 
ß
,但大写 
ß
 会导致 
SS
!)。

如果您想在编译时比较常量字符串类型,这在打字稿中特别有用,因为没有基于区域设置的实用程序类型!

type AreStringsEqual<A extends string, B extends string> = Uppercase<Lowercase<A>> extends Uppercase<Lowercase<B>> ? true : false
    

-3
投票
它从不依赖于浏览器,因为它只涉及 JavaScript。 两者都会根据需要更改的字符数量给出性能(翻转大小写)

var areStringsEqual = firstString.toLowerCase() === secondString.toLowerCase(); var areStringsEqual = firstString.toUpperCase() === secondString.toUpperCase();
如果您使用 @adeneo 准备的测试,您会觉得它依赖于浏览器,但要进行一些其他测试输入,例如:

"AAAAAAAAAAAAAAAAAAAAAAAAAAAA"

"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
并比较。

Javascript 性能取决于浏览器是否存在某些 DOM API 或任何 DOM 操作/交互,否则对于所有纯 JavaScript,它将提供相同的性能。

© www.soinside.com 2019 - 2024. All rights reserved.