如何在JavaScript中将混合Unicode字符(含代理对)拆分至数组
问题
现有如下字符串数组:
unicodes = ["\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d"]
希望将每个Unicode字符(包括U+FFFF以上的代理对组合字符)单独存入数组,最终形式如:
["\ua7a8","\ua7a9","\ud800\udf30"]
尝试了以下代码:
unicodes = ["\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d"] var collectedUnicodes = [] for (let i=0; i < unicodes.length; i++) { collectedUnicodes = collectedUnicodes.concat(unicodes[i].split("")) } console.log(collectedUnicodes)
返回结果中,U+FFFF以上的Unicode代理对被拆分为两个独立元素:
['Ꞩ', 'ꞩ', '\uD800', '\uDF30', '\uD800', '\uDF31', '\uD800', '\uDF32', '\uD800', '\uDF33', '\uD800', '\uDF34', '\uD800', '\uDF35', '\uD800', '\uDF36', '\uD800', '\uDF37', '\uD800', '\uDF38', '\uD800', '\uDF39', '\uD800', '\uDF3A', '\uD800', '\uDF3B', '\uD800', '\uDF3C', '\uD800', '\uDF3D']
需要将代理对组合的完整Unicode字符作为单个数组元素,如何正确实现?
解决方案
方法1:使用ES6扩展运算符(...)
扩展运算符会自动识别Unicode代理对,将其作为单个元素处理:
const unicodes = ["\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d"]; const collectedUnicodes = []; for (const str of unicodes) { collectedUnicodes.push(...str); } console.log(collectedUnicodes);
方法2:使用Array.from()
Array.from()同样能正确处理Unicode代理对,将字符串转换为包含完整字符的数组:
const unicodes = ["\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d"]; const collectedUnicodes = []; for (const str of unicodes) { collectedUnicodes = collectedUnicodes.concat(Array.from(str)); } console.log(collectedUnicodes);
方法3:使用正则表达式匹配
通过正则表达式匹配所有Unicode字符(包括代理对):
const unicodes = ["\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d"]; const collectedUnicodes = []; // 匹配所有Unicode字符的正则 const unicodeRegex = /[\s\S]/gu; for (const str of unicodes) { collectedUnicodes = collectedUnicodes.concat(str.match(unicodeRegex)); } console.log(collectedUnicodes);
原理说明
原来的split("")方法是基于UTF-16编码的代码单元拆分的,而U+FFFF以上的Unicode字符在UTF-16中是用两个代码单元(代理对)表示的,所以会被拆分成两个元素。而上面的三种方法都能识别完整的Unicode字符(包括代理对组合的字符),从而将每个完整字符作为数组的一个元素。
内容的提问来源于stack exchange,提问作者karl-police
相关产品推荐
相关产品推荐

