什么是代理对?

10
我在一个JavaScript开源项目中发现了这段代码。
validator.isLength = function (str, min, max) 
    // match surrogate pairs in string or declare an empty array if none found in string
    var surrogatePairs = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g) || [];
    // subtract the surrogate pairs string length from main string length
    var len = str.length - surrogatePairs.length;
    // now compare string length with min and max ... also make sure max is defined(in other words, max param is optional for function)
    return len >= min && (typeof max === 'undefined' || len <= max);
};

据我所知,上述代码检查字符串的长度但未考虑代理对。因此:
  1. 我的代码理解正确吗?

  2. 什么是代理对?

到目前为止,我只知道这与编码有关。


1
复合字符和代理对的区别 - phuclv
4个回答

22
  1. 是的。您的理解是正确的。该函数返回字符串中 Unicode 代码点的长度。

  2. JavaScript 使用 UTF-16 对其字符串进行编码。这意味着用两个字节(16 位)来表示一个 Unicode 代码点。

    现在,Unicode 中有一些字符(如表情符号)具有非常高的代码点,因此无法使用 2 个字节(16 位)存储它们,因此需要将它们编码为两个 UTF-16 字符(4 个字节)。这些被称为代理对。

请尝试此操作

var len = "".length // There is an emoji in the string (if you don’t see it)

vs

var str = ""
var surrogatePairs = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g) || [];
var len = str.length - surrogatePairs.length;

在第一个例子中,len将会是2,因为表情符号由两个UTF-16字符组成。在第二个例子中,len将会是1。
你可能想要阅读Joel Spolsky的文章《关于Unicode和字符集绝对最少的知识(不容有任何借口)》,了解更多信息。 链接

5
针对你的第二个问题: 1. 什么是Java中的“代理对”? “代理对”这个术语指的是使用UTF-16编码方案来编码具有高代码点的Unicode字符的一种方法。
在Unicode字符编码中,字符被映射到0x0至0x10FFFF之间的值。
在内部,Java使用UTF-16编码方案来存储Unicode文本字符串。在UTF-16中,使用16位(两个字节)代码单元。由于16位只能包含范围从0x0到0xFFFF的字符,因此需要使用一些额外的复杂性来存储超出此范围的值(0x10000至0x10FFFF)。这是使用代理对作为代码单元对来完成的。
代理代码单元分为两个范围,称为“低代理”和“高代理”,具体取决于它们是否允许在两个代码单元序列的开头或结尾。
2. https://msdn.microsoft.com/en-us/library/windows/desktop/dd374069%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 希望这可以帮助你。

虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。 - Karl-Bjørnar Øie
1
接受并更改。 - Eran Yogev

2
在Unicode中,每个字符(包括表情符号)都有一个独特的编号。UTF-16将这些编号编码为16位块,并且使用16位,我们可以制作65,536种不同的组合。
如果我们有一个“Unicode编号”为70,000的字符怎么办?UTF-16针对这种情况有一个算法。首先,它从70,000中减去65,536,得到4464。然后将此数字转换为二进制,并在左侧添加零,直到有20个数字:
00000001000101110000
然后,它分割这个数字:0101110000 0000000100 然后将这些数字加上55,296和55,320,得到十进制的55,664和55,324。这些数字分别是高代理项和低代理项。
然后,UTF-16将这两个数字(它们小于2^16)放入连续的两个8位块中。这里有一个重要的点:55,296(55,296 + 2^10)和55,320(55,320 + 2^10)之间的点并不代表任何特定的字符。因此,如果UTF-16遇到此间隔内的数字,则知道存在第二个块,并且这只是一个字符的一半。
希望这能有所帮助。

-4

请重新阅读您的链接参考文献。“在UTF-16中,范围为U+0000—U+D7FF和U+E000—U+FFFD的字符存储为单个16位单位。非BMP字符(范围为U+10000—U+10FFFF)存储为“代理对”,即两个16位单位...”因此,在UTF-16中,一个字符将是16位或者如果需要代理对,则为32位。没有字符存储为8位。 - Steven Rumbalski
我点赞了这个答案,因为这个参考资料提供了有关UTF-16中代理对如何工作的最佳描述。简而言之,U+0000-U+D7FF和U+E000-U+FFFD被编码为2个字节;超过U+10000的任何内容都被编码为由该范围中间的空隙中选取的两个字符组成的“代理对”。单独的代理字符是无效的,这就是为什么上面的正则表达式在https://dev59.com/WI7da4cB1Zd3GeqP8Awf#31986749中有效(为了简洁起见,我省略了一些复杂性)。 - kierantop

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