js转码与解码emoji

想要为他人鼓掌👏?想用emoji来表达工作🧑‍💻?想及时更新状态💃?在人类和身体分类里有各种手势、角色cosplay、人物活动,你的日常活动全都可以找到👀。用生动的emoji来表达有趣的一天🚴,请继续往下探索吧👇。

这是主分类👌 人类和身体的表情符号列表页面。它包括16个子分类,例如:🖐 手掌张开, 👌 几根手指, 👈 一根手指, 👍 合上手掌, 🤝 双手, ✍️ 动作手, 👃 部分身体, 👦 人物, 🙋 人物手势, 👨‍🍳 人物角色, 🎅 虚构人物, 🏃 人物活动, 🚴 人物运动, 🛌 人物休息, 👨‍👩‍👧‍👦 家庭, 👣 人物标记。您可以单击下面的链接查看详细信息并复制表情符号。

javascript 正常的英文编码是 utf-8 的,mysql 默认存的也是这种编码,而 emoji 表情是 utf-16 的,这就导致了 db 存储 emoji 会有问题,所以最好的方式是,把 emoji 先转成 utf-8 的这种实体编码,存到数据库里,要使用的时候,从 db 拿出来,再解码成 utf-16 的形式。

原文章

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
{
// 表情转码
utf16toEntities(str) {
const patt = /[\ud800-\udbff][\udc00-\udfff]/g; // 检测utf16字符正则
str = str.replace(patt, (char) => {
let H;
let L;
let code;
let s;

if (char.length === 2) {
H = char.charCodeAt(0); // 取出高位
L = char.charCodeAt(1); // 取出低位
code = (H - 0xD800) * 0x400 + 0x10000 + L - 0xDC00; // 转换算法
s = `&#${code};`;
} else {
s = char;
}

return s;
});

return str;
},
// 表情解码
entitiestoUtf16(strObj) {
const patt = /&#\d+;/g;
const arr = strObj.match(patt) || [];

let H;
let L;
let code;

for (let i = 0; i < arr.length; i += 1) {
code = arr[i];
code = code.replace('&#', '').replace(';', '');
// 高位
H = Math.floor((code - 0x10000) / 0x400) + 0xD800;
// 低位
L = ((code - 0x10000) % 0x400) + 0xDC00;
code = `&#${code};`;
const s = String.fromCharCode(H, L);
strObj = strObj.replace(code, s);
}
return strObj;
}
}

使用示例

1
2
3
4
5
6
7
8
9
const s = 'test emoji 👇👉👈🙌';
const dbSaveStr = utf16toEntities(s);
// 结果是: 'test emoji &#128071;&#128073;&#128072;&#128588;' 这样子的实体编码字符串存到db就没问题了

// 要使用时,想数据库中拿到上面的存储记录
cosnt dbOutStr = 'test emoji &#128071;&#128073;&#128072;&#128588;' ;
// 然后将其中的emoji转码成表情使用
const ret = entitiestoUtf16(dbOutStr )
// 得到: 'test emoji 👇👉👈🙌';

将 emoji 转成 UTF-16

这是更通用的方式,因为上面那种只是在 web 端能显示,如果是要存到 db,给 c++获取数据再客户端 app 展示,就行不通了。必须转成 unicode-16 才行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// http://www.2ality.com/2013/09/javascript-unicode.html
function toUTF16(codePoint) {
var TEN_BITS = parseInt("1111111111", 2);
function u(codeUnit) {
return "\\u" + codeUnit.toString(16).toUpperCase();
}

if (codePoint <= 0xffff) {
return u(codePoint);
}
codePoint -= 0x10000;

// Shift right to get to most significant 10 bits
var leadSurrogate = 0xd800 + (codePoint >> 10);

// Mask to get least significant 10 bits
var tailSurrogate = 0xdc00 + (codePoint & TEN_BITS);

return u(leadSurrogate) + u(tailSurrogate);
}

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// using codePointAt, it's easy to go from emoji
// to decimal and back.
// Emoji to decimal representation
"😀".codePointAt(0) > 128512;

// Decimal to emoji
String.fromCodePoint(128512) > "😀";

// going from emoji to hexadecimal is a little
// bit trickier. To convert from decimal to hexadecimal,
// we can use toUTF16.
// Decimal to hexadecimal
toUTF16(128512) > "\uD83D\uDE00";

// Hexadecimal to emoji
"\uD83D\uDE00" > "😀";

判断字符串是否为 emoji 字符

1
const emojiReg = /(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff])[\ufe0e\ufe0f]?(?:[\u0300-\u036f\ufe20-\ufe23\u20d0-\u20f0]|\ud83c[\udffb-\udfff])?(?:\u200d(?:[^\ud800-\udfff]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff])[\ufe0e\ufe0f]?(?:[\u0300-\u036f\ufe20-\ufe23\u20d0-\u20f0]|\ud83c[\udffb-\udfff])?)*/;