当前位置: 首页 > 科技观察

JavaScript的内部字符编码是UCS-2还是UTF-16

时间:2023-03-12 12:20:31 科技观察

关于JavaScript使用UCS-2还是UTF-16的问题我找了半天也没找到权威的答案。我决定自己研究一下。这个答案来自于你对JavaScript引擎或JavaScript语言的理解。1.著名的BMP(BasicMultilingualPlane)Unicode标识符使用一个明确的名字和一个整数作为它的代码点(codepoint)。例如“?”字符的码位可以用“版权标志”和U+00A9(0xA9,也可以写成十进制的169)来表示。Unicode字符分为17组平面,每组平面有2^16(65,536)个码位。有些代码点没有分配字符,有些代码点被保留并成为私有的,有些代码点总是被保留,作为没有字符的标志。每个代码点都可以用十六进制的xy0000到xyFFFF来表示,其中xy代表一个十六进制值,从00到10。这个第一个位置(当xy为00时)被称为BMP(BasicMultilingualPlane)。它包含从U+0000到U+FFFF的最常用代码点。这里需要补充一点额外的位面知识,还有一个术语表。引用自:wikipedia剩下的16个平面(U+100000到U+10FFFF)称为补充平面。我不会在这里讨论它;只需记住两个概念:BMP字符和非BMP字符,后者也称为增补字符。二、UCS-2和UTF-16的区别UCS-2和UTF-16都是Unicode字符编码方式。UCS-2(UniversalCharacterSetof2bytes)是一种固定长度的编码格式,只需要编码为16字节的代码单元来表示代码点。对于0到0xFFFF(BMP)的大部分范围,这样的表示将产生与UTF-16相同的结果。UTF-16(Unicode转换格式16)是UCS-2的扩展,允许表示比BMP范围内更多的字符。它是一种可变长度格式,其中每个代码点都可以使用1位或2位16字节代码单元表示。可以这样编码的码位在0到0x10FFFF之间。比如在UCS-2和UTF-16中,BMP字符U+00A9版权符号(?)都可以编码为:0x00A9。这里补充一下UCS-2、UCS-4、BMPCPU处理多字节数:“bigendian”(大端)和“littleendian”(小端)。简单理解就是一个Unicode编码,比如6C49,在文件里写6C49或者496C,两种方式,前者叫“大尾”,后者叫“小尾”。UCS可分为两种格式:UCS-2和UCS-4。UCS-2使用两个字节进行编码,UCS-4使用4个字节(实际上只有31位,最高位必须为0)进行编码。转换关系:UCS-4高两个字节为0码位的称为BMP;UCS-4BMP去掉前两个零字节得到UCS-2;UCS-2加上两个零字节以获得UCS-4中的BMP。3.Surrogatepairs对于BMP以外的字符,比如U+1D306,四行居中(其实不好翻译:tetragramforcenter,?),只能用两个UTF-16的16字节编码:0xD8340XDF06。这称为代理对。值得注意的是,代理对只代表一个字符。补充一点,代理对的概念其实是指上面的UTF-16编码,使用了两个16字节的编码。那是因为一个UTF-16编码是不够的,那么应该用2个UTF-16编码来表示更多的字符。然后会发生这种情况:过去一个字符的2个字节的空间变成了4个字节的空间。所以规定只用2个UTF-16编码来表示一定范围内的一个字符。这种方式称为代理对,其余仍用2个字节表示。surrogatepair中的第一个字符单元总是在0xD800和0xDBFF之间,称为highsurrogate或leadsurrogate(highsurrogate或leadsurrogate,暂且先找出专业术语再翻译)。第二个字符单元始终在0xDC00和0xDFFF之间,称为低代理项或尾代理项。UCS-2不支持代理对,因此要表示前面的字符需要使用2个分隔字符。4.代码点和代理对之间的转换TheUnicodeStandard3.0(pdf)的第3.7节定义了一种转换算法。假设:大于0xFFFF的码点C由代理对表示:H=Math.floor((C-0x10000)/0x400)+0xD800L=(C-0x10000)%0x400+0xDC00转换后公式转换,比如从一个代理对到一个Unicode码点C,可以用公式:C=(H-0xD800)*0x400+L-0xDC00+0x100005.好的,那么关于JavaScript的编码问题?ECMAScript中定义了如何解释字符的问题。在level3或更高级别的实现中,遵循国际标准,以Unicode3.0标准或更新的标准,以及ISO/IEC10646-1和UCS-2或UTF-16作为编码格式。如果采用的ISO/IEC10646-1本身没有指定,则标识为BMP自己的,设置300(这里不懂)。如果没有使用其他编码,它将被编码为UTF-16。也就是说,JavaScript引擎被允许使用UCS-2或UTF-16进行编码。然后根据具体的部分,认为引擎中的编码需要一些UTF-16的知识。当然,内部引擎对大多数JavaScript开发人员没有实际影响。关于JavaScript如何看待字符的更多有趣发现,有一段话:尽管在本文档的其余部分,字符单元和文字字符将使用16位无符号值表示,用于表示单个16位文本单元。Unicode字符将使用抽象的语言或印刷单位(可以超过16位,因此可以由多个代码单元表示)来表示。代码点可以用Unicode标准值表示。组合字符序列的组件可能具有单独的“Unicode字符”,即使用户可能将整个序列视为单个字符。可能需要重新翻译,原文在本文档的其余部分,短语代码单元和单词字符将用于指代用于表示单个16位文本单元的16位无符号值。短语Unicode字符将用于指代由单个Unicode标量值(可能长于16位,因此可能由多个代码单元表示)表示的抽象语言或印刷单位。短语代码点指的是这样一个Unicode标量值。Unicode字符仅指由单个Unicode标量值表示的实体:组合字符序列的组件仍然是单独的“Unicode字符”,即使用户可能会想到整个序列作为单个字符。JavaScript使用单个字符来处理字符单元通常被认为是一组Unicode字符。当使用BMP范围之外的Unicode字符时,这会产生一些不良后果。例如,一个代理对由2个字符单元组成:'?'.length==2,尽管这里只有一个Unicode字符。如果是字符,代理对将暴露部分:'?'=='\uD834\uDF06'。你在想什么?对于这种方法,至少是UCS-2的替代方法(不同之处在于UCS-2不允许代理字符,而JavaScript字符串允许)。您可以将其视为像UTF-16一样工作,特别是允许使用两部分方法,允许代理项的这种错误排序,并且代理项作为单独的字符公开。我觉得你更容易理解,这种行为叫做“UCS-2proxymethod”(UCS-2withsurrogates,不好翻译,也可以理解为UCS-2withsurrogates)。像UCS-2这样的行为对整个语言的影响更大,因为补充字符范围的正则表达式比支持UTF-16的正则表达式更难编写。代理对只是单个Unicode字符的重组,以便在浏览器中显示(在布局期间)。这发生在JavaScript引擎的范围之外。为了证明这一点,可以在document.write().document.write('\uD834');document.write('\uDF06');时分别写一个高阶代理和一个状态代理字符。也将在结束后读取Renderedasapattern:?.6.结论JavaScript引擎在内部可以自由使用UCS-2或UTF-16。我所知道的大多数引擎都使用UTF-16,无论它们以何种方式实现它,它都只是一个具体的实现,它不会影响语言的特性。那么对于ECMAScript/JavaScript语言本身来说,效果是通过UCS-2实现的,而不是UTF-16。如果在任何时候您需要对Unicode字符进行编码,以便能够在必要时用拆分代理替换它,您也可以免费试用我的JavaScript转义工具。如果您想获取JavaScript字符串中Unicode字符的长度,或创建基于非BMPUnicode代码点的字符串,您可以使用Punycode.js实用方法将UCS-2字符串转换为UTF-16代码点。//`String.length`只是统计所以Unicode字符punycode.ucs2.decode('?').length;//1//`String.fromCharCode`允许你直接使用非分离代理punycode.ucs2.encode([0x1D306]);//'?'punycode.ucs2.encode([119558]);//'?'ECMAScript6会支持字符串中一些新的编码序列(现在好像可以了,大家可以查看为了简单了解资料),它被称为Unicode代码点转义,例如:\u{1D306}。此外,它将定义String.fromCodePoint和String#codePointAt,它们都接受代码点而不是字符单元感谢:Jonas'nlogax'Westerlund、Andrew'bobince'Clover和TabAtkinsJr..他们启发我进行调查并提供帮助我。提示:如果您喜欢阅读有关JavaScript的内部字符编码的文章,可以查看JavaScripthasaUnicodeproblem,它更详细地解释了实际问题,并提供了解决方法。