【问题标题】:What's the point of String.normalize()?String.normalize() 的意义何在?
【发布时间】:2020-11-10 18:38:43
【问题描述】:

在查看 JavaScript 概念时,我发现了String.normalize()。这不是 W3School 的“JavaScript String Reference”中出现的内容,因此,这就是我之前可能错过的原因。

我在HackerRank 中找到了有关它的更多信息,其中指出:

返回一个包含 Unicode 规范化形式的字符串 调用字符串的值。

举个例子:

var s = "HackerRank";
console.log(s.normalize());
console.log(s.normalize("NFKC"));

作为输出:

HackerRank
HackerRank

另外,在GeeksForGeeks:

string.normalize() 是 javascript 中的一个内置函数,它是 用于返回给定输入字符串的 Unicode 规范化形式。

举个例子:

<script> 
  
  // Taking a string as input. 
  var a = "GeeksForGeeks"; 
    
  // calling normalize function. 
  b = a.normalize('NFC') 
  c = a.normalize('NFD') 
  d = a.normalize('NFKC') 
  e = a.normalize('NFKD') 
    
  // Printing normalised form. 
  document.write(b +"<br>"); 
  document.write(c +"<br>"); 
  document.write(d +"<br>"); 
  document.write(e); 
    
</script> 

作为输出:

GeeksForGeeks
GeeksForGeeks
GeeksForGeeks
GeeksForGeeks

也许给出的例子真的很糟糕,因为它们不允许我看到任何变化。

我想知道...这种方法有什么意义?

【问题讨论】:

  • 首先让我说 w3schools.com 不是官方参考。它与 W3C 没有任何关系。这是一个合适的资源:@​​987654324@
  • 我知道@ChrisG,但内容通常非常好。
  • 不,它不是,它不像以前那么糟糕,但这个社区尤其是仍在遭受它的折磨。我猜这个问题的存在就证明了我的观点?
  • String.prototype.normalize() 在技术意义上是正确的,因为normalize() 是您调用实例的动态方法,而不是类本身。 normalize() 的重点是能够比较看起来相同但不包含相同字符的字符串,如 MDN 上的示例代码所示。
  • 令人困惑的是,任何人都可以为 normalize() 编写“文档”——一个适用于 Unicode 字符串的函数——通过演示纯 ASCII 字符串......

标签: javascript string unicode normalization


【解决方案1】:

MDN documentation 中所述,String.prototype.normalize() 返回字符串的 Unicode 规范化形式。这是因为在 Unicode 中,某些字符可以有不同的表示代码。

这是示例(取自 MDN):

const name1 = '\u0041\u006d\u00e9\u006c\u0069\u0065';
const name2 = '\u0041\u006d\u0065\u0301\u006c\u0069\u0065';

console.log(`${name1}, ${name2}`);
// expected output: "Amélie, Amélie"
console.log(name1 === name2);
// expected output: false
console.log(name1.length === name2.length);
// expected output: false

const name1NFC = name1.normalize('NFC');
const name2NFC = name2.normalize('NFC');

console.log(`${name1NFC}, ${name2NFC}`);
// expected output: "Amélie, Amélie"
console.log(name1NFC === name2NFC);
// expected output: true
console.log(name1NFC.length === name2NFC.length);
// expected output: true

如您所见,字符串 Amélie 是两种不同的 Unicode 表示形式。通过规范化,我们可以将两种形式简化为同一个字符串。

【讨论】:

    【解决方案2】:

    这取决于字符串的用途:通常您不需要它(如果您只是从用户那里获取输入,然后将其交给用户)。但是要检查/搜索/用作键/等。这样的字符串,您可能需要一种独特的方式来识别相同的字符串(从语义上讲)。

    主要问题是您可能有两个在语义上相同的字符串,但具有两种不同的表示形式:例如一种带有重音字符[一个代码点],另一种带有带有重音符号的字符[一个字符代码点,一个用于组合重音符号]。用户可能无法控制输入文本的发送方式,因此您可能有两个不同的用户名或两个不同的密码。但是,如果您破坏数据,您可能会得到不同的结果,具体取决于初始字符串。用户不喜欢。

    另一个问题是关于组合字符的唯一顺序。你可能有一个重音和一个低尾音(例如 cedilla):你可以用几种组合来表达:“pure char, tail, accent”, “pure char, accent, tail”, “char+tail, accent”, “ char+accent, cedilla"。

    而且您可能会遇到退化的情况(特别是如果您从键盘键入):您可能会得到应该删除的代码点(您可能有一个无限长的字符串,可能相当于几个字节。

    在任何情况下,为了对字符串进行排序,您(或您的库)需要一个规范化的形式:如果您已经提供了正确的形式,则库不需要再次对其进行转换。

    所以:您希望相同(语义上)字符串具有相同的 unicode 代码点序列。

    注意:如果您直接使用 UTF-8,您还应该注意 UTF-8 的特殊情况:相同的代码点可以以不同的方式写入 [使用更多字节]。这也可能是一个安全问题。

    K 通常用于“搜索”和类似任务:CO2 和 CO₂ 将以相同的方式解释,但这可能会改变文本的含义,因此它应该经常只在内部使用,用于临时任务,但保留原文。

    【讨论】:

      【解决方案3】:

      字符串的规范化并非 JavaScript 独有 - see for instances in Python。参数的有效值由Unicode 定义(更多关于Unicode normalization)。

      谈到 JavaScript,请注意有 String.normalize()String.prototype.normalize() 的文档。正如@ChrisG 提到的那样

      String.prototype.normalize() 在技术意义上是正确的,因为 normalize() 是您在实例上调用的动态方法,而不是类 本身。 normalize() 的重点是能够比较字符串 看起来一样,但不包含相同的字符,如图所示 MDN 上的示例代码。

      然后,说到它的用法,发现有一个great example of the usage of String.normalize()

      let s1 = 'sabiá';
      let s2 = 'sabiá';
      
      // one is in NFC, the other in NFD, so they're different
      console.log(s1 == s2); // false
      
      // with normalization, they become the same
      console.log(s1.normalize('NFC') === s2.normalize('NFC')); // true
      
      // transform string into array of codepoints
      function codepoints(s) { return Array.from(s).map(c => c.codePointAt(0).toString(16)); }
      
      // printing the codepoints you can see the difference
      console.log(codepoints(s1)); // [ "73", "61", "62", "69", "e1" ]
      console.log(codepoints(s2)); // [ "73", "61", "62", "69", "61", "301" ]
      

      因此,虽然此示例中的 saibásaibá 在人眼看来是相同的,或者即使我们使用了 console.log(),但我们可以看到,在比较它们时,如果没有归一化,我们会得到不同的结果。然后,通过分析代码点,我们发现它们是不同的。

      【讨论】:

        【解决方案4】:

        这里解释得很漂亮 --> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize

        简短回答:关键是,字符通过 ascii、utf-8 等编码方案表示(我们主要使用 UTF-8)。有些字符有不止一种表示形式。所以 2 个字符串可能会呈现类似的效果,但它们的 unicode 可能会有所不同!所以字符串比较可能在这里失败!所以我们使用 normaize 来返回单一类型的表示

        // source from MDN
        
        let string1 = '\u00F1';                           // ñ
        let string2 = '\u006E\u0303';                     // ñ
        
        string1 = string1.normalize('NFC');
        string2 = string2.normalize('NFC');
        
        console.log(string1 === string2);                 // true
        console.log(string1.length);                      // 1
        console.log(string2.length);                      // 1
        

        【讨论】:

          【解决方案5】:

          这里已经有一些很好的答案,但我想举一个实际的例子。

          我喜欢翻译圣经作为一种爱好。在我的价格范围内(免费),我对野外的抽认卡选项不太兴奋,所以我自己做了。问题是,有不止一种方法可以在 Unicode 中使用希伯来语和希腊语来获得完全相同的东西。例如:

          בָּא
          בָּא
          

          这些在您的屏幕上看起来应该是相同的,并且实际上它们是相同的。但是,第一个是在 dagesh(字母中间的点)之前用 qamats(它下面的小 t 形的东西)输入的,第二个是在 qamats 之前用 dagesh 输入的。现在,既然你只是在读这个,你不在乎。你的网络浏览器不在乎。但是当我的抽认卡比较两者时,它们就不一样了。对于幕后的代码,无异于说“中心”和“中心”是一样的。

          类似地,在希腊语中:

          ἀ
          ἀ
          

          这两个应该看起来几乎相同,但顶部是一个 Unicode 字符,第二个是两个 Unicode 字符。最终会在我的抽认卡中输入哪一个将取决于我坐在哪个键盘上。

          当我添加抽认卡时,不管你信不信,我并不总是输入 100 个单词的词汇表。这就是上帝给我们电子表格的原因。有时我从中导入列表的地方以一种方式进行,有时他们以另一种方式进行,有时他们混合使用。但是当我打字时,我并不想记住 dagesh 或 quamats 出现的顺序,或者是否将重音作为单独的字符输入。不管我是否记得先输入 dagesh,我都想得到正确的答案,因为无论哪种方式,实际上它都是相同的答案。

          所以我在保存抽认卡之前对顺序进行了规范化,在检查之前对顺序进行了规范化,结果是无论我输入哪种方式,它都正确!

          如果你想查看结果:

          https://sthelenskungfu.com/flashcards/

          您需要一个 Google 或 Facebook 帐户才能登录,以便它可以跟踪进度等。据我所知(或关心),目前只有我和我女儿使用它。

          它是免费的,但永远处于测试阶段。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-03-29
            • 2012-12-09
            • 2016-04-14
            • 2010-10-27
            • 2023-03-04
            • 2011-09-06
            • 2012-11-14
            相关资源
            最近更新 更多