当比较 Unicode 字符时,JavaScript 字符串比较会失败。

18

我想在JavaScript中比较两个字符串,它们相同,然而等号运算符==返回false。一个字符串包含了一个特殊字符(例如丹麦语的å)。

JavaScript代码:

var filenameFromJS = "Designhåndbog.pdf";
var filenameFromServer = "Designhåndbog.pdf";

print(filenameFromJS == filenameFromServer); // This prints false why?

解决方案 像 slevithan 指出的那样,对 Unicode 进行规范化对我很有效。

我分叉了我的原始 jsfiddle,并使用 slevithan 建议的规范化库制作了一个版本。链接:http://jsfiddle.net/GWZ8j/1/


1
请查看关于=====的文章:https://dev59.com/Z3RC5IYBdhLWcg3wROpQ - Steve
5
当两个操作数的类型相同时,使用宽松比较或严格比较没有区别。 - PointedEars
1
这也非常有用: https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/ (开发人员需了解的有关Unicode和字符集的绝对最少知识) - GrahamMc
5个回答

15

与其他人的说法不同,这与编码无关。相反,你的两个字符串使用不同的码位来呈现相同的可视字符。

为了正确解决这个问题,你需要在比较之前对这两个字符串执行Unicode规范化。不幸的是,JavaScript没有内置这个功能。这里有一个可以为你执行规范化的JavaScript库:https://github.com/walling/unorm


1
哦,我希望不会得到这个答案 :-) 我只是错过了显而易见的东西,不需要为这个简单的任务使用库。谢谢你的回答,我会尝试一下。 - tougher
你是对的,我忽略了CC 8A是UTF-8编码序列U+30A COMBINING RING ABOVE,它前面有一个a。另一个字符串是C3 A5,它编码了UTF-8中的U+00E5 LATIN SMALL LETTER A WITH RING ABOVE。据我所知,Mac OS更喜欢使用组合字符,而其他操作系统更喜欢使用单个字形形式。服务器应该能够转换其中任何一种形式,因此不需要大型客户端库。 - PointedEars
PointedEars,这并不一定是可能或理想的。例如,您可能不想执行服务器往返以执行字符串比较,或者您可能正在服务器上使用JavaScript。@Tougher,有一个提案将Unicode规范化添加到未来版本的JavaScript中。请参见strawman:unicode_normalization - slevithan
1
现在JS原生支持String#normalize()方法。 - Kaiido

6
JavaScript 相等运算符 == 在以下情况下似乎会失败。在所有情况下,这都是程序员的错误,而不是 JavaScript 的 bug。
  1. 两个字符串不包含相同数量和序列的字符。

  2. 一个字符串中存在空格或换行符,在其前面、中间或后面。对两个字符串使用 trim() 运算符,并仔细查看两个字符串。

  3. 意外的类型转换。程序员比较了不兼容的数据类型。

  4. 存在看起来相同但实际上不同的 Unicode 字符。


+1,因为这个答案比被接受的那个更详细,并且不包含任何关于nodeJS或jQuery的内容。 - unexist
在这种情况下,数字4是罪魁祸首。 - vahanpwns
不同的Unicode规范化并不涉及不同的字符,而是指使用不同的Unicode代码点序列来引用相同的字符。 - James

1

UTF-8 是一个复杂的东西。字符集对于像á、é等字符有两个不同的编码。正如您在 URL 编码版本中看到的那样,这些字符所组成的十六进制字节在两个版本中是不同的。

请参阅this答案以获取更多信息。


1
顺便提一下:Unicode 不等同于 UTF-8。Unicode 是一个字符集标准,包含多种编码方式;而 UTF-8 则是其中一种编码方式。 - PointedEars
1
现在你说UTF-8是一个字符集,但实际上它不是。我也非常确定你的前提是错误的:UTF-8代码序列不能以0xCC开头。 - PointedEars
1
你说得对,我应该称其为“编码”,因为它在(http://www.w3.org/TR/html4/charset.html)中出现。然而,HTML代码是`<meta charset=UTF-8>(HTML5)或<meta http-equiv=Content-Type content='text/html; charset=UTF-8'>`,所以有点误导人。 - user2428118
1
是的,我想我们将不得不接受早期互联网草案(我指的是RFC 822和相关文件)中的那个错误很长一段时间。 - PointedEars
1
我对0xCC的理解是错误的。Richard Ishida的优秀Unicode工具证明了这一点。 - PointedEars

0

让浏览器为您规范化Unicode。这种方法对我很有效:

function normalizeUnicode(s) {
    let div = $('<div style="display: none"></div>').html(s).appendTo('body');
    let res = div.html();
    div.remove();
    return res;
}

normalizeUnicode(unicodeVal1) == normalizeUnicode(unicodeVal2)

0

我曾经也遇到过这个问题。

加上

<meta charset="UTF-8">

将 JSON 字符串嵌入 HTML 文件中导致了问题。

在我的情况下,模板引擎将 JSON 字符串嵌入到 HTML 文件中。该字符串是 Unicode 编码的。

虽然模板也是 Unicode 文件,但 JS 引擎将我写入模板的字符串视为 Latin-1 编码的字符串,直到我添加了 meta 标签。

我正在将输入的字符串与 JSON 对象项之一进行比较(location.title == "Mühle"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接