未加星标

JavaScript emoji utils

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二03 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏
写在前面

javascript的字符串处理貌似不难,直到遇上了emoji:


JavaScript emoji utils

javascript-emoji-issues

??发生了什么? 到底怎么回事?

得从Unicode编码说起……

一.Unicode编码 The Unicode codepoint range goes from U+0000 to U+10FFFF which is over 1 million symbols, and these are divided into groups called planes. Each plane is about 65000 characters (16^4). The first plane is the Basic Multilingual Plane (U+0000 through U+FFFF) and contains all the common symbols we use everyday and then some. The rest of the planes require more than 4 hexadecimal digits and are called supplementary planes or astral planes.

也就是说,Unicode支持的编码范围是 U+0000 到 U+10FFFF ,能对应100多万个符号( 0x10FFFF === 1114111 )。这些符号被分组归入16个 平面(panel) ,所以每个平面放65536( 16^4 === 65536 )个

其中,常用符号都放在第一个平面( U+0000 到 U+FFFF )里,所以称之为 基本多语言平面(Basic Multilingual Plane,也简称BMP) ,其余的平面中的 码位值(codepoint,即符号对应的Unicode编码值) 都大于4位(16进制),称为 辅助平面(supplementary plane)

P.S.辅助平面还有个看起来 很厉害 的名字,叫astral plane(星界?星界位面?)

I have no idea if there’s a good reason for the name “astral plane.” Sometimes, I think people come up with these names just to add excitement to their lives.

此外,基本多语言平面里 65536 个位置的入住率并不是 100% , 专门空出来一些位置 以备不时之需,比如新增特殊含义符号,或者扩展

比如UTF-16中 代理对儿(surrogate pairs) 的概念,即用两个4位(16进制)的小码位值表示一个大码位值(大于4位),算是一种从基本多语言平面到辅助平面的映射,之所以能这样做,就是因为:

基本多语言平面内,从U+D800到U+DFFF之间的码位区段是永久保留不映射到Unicode字符。UTF-16就利用保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位进行编码。

二.JavaScript中的Unicode

JS中的Unicode字符有3种表示方法:

'A' === '\u0041' === '\x41' === '\u{41}'

其中 \x 仅用于 U+0000 到 U+00FF , \u 适用于任意Unicode字符( U+0000 到 U+10FFFF ),但大于4位(大于 U+FFFF )的话,就要用花括号( {} )把十六进制序列包起来:

The \x can be used for most (but not all) of the Basic Multilingual Plane, specifically U+0000 to U+00FF. The \u can be used for any Unicode characters. The curly braces are required if there are more than 4 hexadecimal digits and optional otherwise.

注意, \u{} 转义语法是在ES 2015中定义的,称之为 UnicodeEscapeSequence 。之前用两个小Unicode来表示一个大Unicode,例如:

'' === '\u{1F4A9}' '' === '\uD83D\uDCA9'

\uD83D\uDCA9 就是代理对儿,形如 <H,L> ,二者的转换关系如下:

let C, L, H; C = 0x1F4A9; // 公式:大Unicode转代理对儿 H = Math.floor((C - 0x10000) / 0x400) + 0xD800; L = (C - 0x10000) % 0x400 + 0xDC00; [H, L].map(v => '\\u' + v.toString(16).toUpperCase()).join('') "\uD83D\uDCA9"

另外,JS中认为一个16位无符号整数值是一个字符,所以一个emoji可能会被认为是多个字符:

The phrase code unit and the word character will be used to refer to a 16-bit unsigned value used to represent a single 16-bit unit of text. Unicode character only refers to entities represented by single Unicode scalar values: the components of a combining character sequence are still individual “Unicode characters”, even though a user might think of the whole sequence as a single character.

P.S.关于JavaScript的Unicode支持以及ES规范的相关内容,见 JavaScript’s internal character encoding: UCS-2 or UTF-16?

正则表达式中的Unicode

既然大Unicode(大于 U+FFFF 的)在JS中用两个小Unicode(代理对儿)来表示,那么自然会写出这样的正则表达式:

> /[\uD83D\uDCA9-\uD83D\uDE0A]/.test('') Uncaught SyntaxError: Invalid regular expression: /[\uD83D\uDCA9-\uD83D\uDE0A]/: Range out of order in character class

报错无法识别这样的range,那 怎样用正则表达式描述大Unicode字符范围 呢?

JS提供了 u flag 来解决这个问题:

u Unicode; treat pattern as a sequence of Unicode code points /[\uD83D\uDCA9-\uD83D\uDE0A]/u.test('') /[-]/u.test('')

类似的, . (点号匹配任意字符)想要匹配代理对儿形式的大Unicode的话,也需要开启u flag:

> /foo.bar/.test('foobar') false > /foo.bar/u.test('foobar') true

P.S. /./u 仅能匹配代理对儿形式的emoji ,其它形式的不行,例如:

> /foo.bar/u.test('foo2bar') false

P.S.更多相关示例,见 Astral ranges in character classes

fromCodePoint与fromCharCode

String.fromCodePoint 与 String.fromCharCode 的区别在于,前者支持更大范围的16进制Unicode编码,例如:

> String.fromCodePoint(0x1F4A9) "" > String.fromCharCode(0x1F4A9) ""

但 fromCodePoint 由ES 2015规范定义,兼容性不如 fromCharCode 好,对于 0x0000-0xFFFF 范围的65536个Unicode字符,建议使用 fromCharCode

三.emoji编码

类似于Unicode,emoji也是一种编码规则,也有对应的规范,还存在很多个版本:

Emoji 12.0 Emoji 11.0 Emoji 5.0 Emoji 4.0 Emoji 3.0 Emoji 2.0 Emoji 1.0

其中 12.0 计划2019年才发布,最新的 11.0 发布于 2018-02-07

像HTML、CSS规范一样,新版规范中新增的emoji不一定都被实现了,并且面临的 兼容性问题比HTML、CSS更恶劣 :

规范版本:emoji规范发版频繁,多版本共存

平台差异:除了Web浏览器环境外,emoji还依赖平台原生支持(各种屏幕显示设备)

依赖Unicode:emoji是在Unicode基础上建立的,依赖Unicode规范

比如从短信复制粘贴到网页输入框,emoji可能就显示不出来或者乱码了,因为native与Web浏览器支持的emoji规范版本或实现程度存在差异。另外,Unicode新规范可能会与已定义的emoji规范有冲突,这时候自然得由emoji规范让步:

Unicode 12.0 is the new version of the Unicode Standard planned for release in March 2019. See Emoji 12.0 for a more complete list of potential emojis for 2019. Note: All emojis listed throughout 2018 are candidates only, and subject to change before a final release.

emoji面临的环境有多恶劣呢?如图:


JavaScript emoji utils

emoji-unicode-platform

回到emoji规范本身,长这样子:

1F600 ; emoji ; L1 ; secondary ; x # V6.1 () GRINNING FACE 1F48F ; emoji ; L1 ; none ; j # V6.0 () KISS

最左边是Unicode码位值,被成功录入Unicode规范的话, U+1F48F 就会对应 KISS 表情:

> '\u{1F48F}' ""

除了这种与Unicode一一对应的emoji,加入Unicode大家庭外,还 有几种特殊的emoji :

variation selector-16 :一个不可见字符( U+FE0F ),表示在它前面的字符应该用emoji显示

zero width joiner :零宽连接符,是一个零宽空格( U+200D ),用来把多个emoji合成为一个emoji

tone modifier:肤色修饰,一种语法,能改变前一个emoji的肤色,语法格式是 <emoji>\ud83c[\udffb-\udfff] ,即 U+D83C 后面跟不同的几个值表示不同的肤色控制

keycap:键帽符号,键帽样式的 0-9 、 # 和 * ,以 U+20E3 结尾

unofficial emoji flag:存在一些非常规国旗emoji,以黑色旗子( U+1F3F4 )开头, 取消符号 ( U+E007F )结尾

例如:

// \ufe0f让黑心字符显示成emoji,连续两个也没关系 ':heart:' === '\u2764\ufe0f\ufe0f' '\u2764\ufe0f' === ':heart:' '\u2764' === '' // 零

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

tags: Unicode,emoji,gt,uD83D,字符,U+0000,character,The,规范,平面
分页:12
转载请注明
本文标题:JavaScript emoji utils
本站链接:https://www.codesec.net/view/597101.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(27)