一种mysql不支持emoji的前端拦截方案

1,184 阅读2分钟

image.png

Emoji表情输入

常用的utf8编码,最多只会达到3字节,如MySQL的utf8编码。但像emoji表情等Unicode是4字节的(UCS-4),在编码为utf8时,也会占用4字节。在MySQL中,就要使用utf8mb4(most bytes 4)编码,否则插入时会报错。

当数据库不支持emoji时,前端如何实现拦截呢?
对于字符过滤,一般我们第一个想到的大多是正则表达式。然而,实际使用中,由于emoji表情的不断增加或正则表达式本身的缺陷,往往达不到过滤的效果(MySQL的utf8编码,并不是所有emoji都不支持,小于4字节的❎就是个例外,我们总不能数据库支持的字符都不给用吧)。

image.png

发现问题

欢迎来到王者荣耀😊😊 字符数量10,字符串长度12

一次开发中,使用了el-input的字符数统计属性show-word-limit,发现输入emoji表情统计到的字符数量和实际看到的字符数量不一致。

el-input

然后,尝试通过字符串分割成数组,再比较长度,发现str.split('')得到的数组长度和统计到的字符数是一样的,但是和看到的字符数量还是不一致。

var str = '欢迎来到王者荣耀😊😊'
var arr = str.split('')
console.log(str.length) // 12
console.log(arr.length) // 12

解决问题

那么,是否可以通过字符串的字符数量和字符串长度来判断是否输入了emoji表情呢?
要验证这个问题,关键的是获取到字符串中字符的数量。

那么如何获取字符串中字符的数量呢,通过研究(百度)发现,分割utf8字符串的正确方法是使用 Array.from(str) 而不是str.split('')。

其实,除了Array.from()之外,还有其他方法也可以实现,如:扩展运算符(...)和解构赋值。

var str = '欢迎来到王者荣耀😊😊'
// Array.from()
var arr2 = Array.from(str)
// 扩展运算符
var arr3 = [...str] 
// 解构赋值
var [...arr4] = str 
console.log(str.length) // 12
console.log(arr2.length) // 10
console.log(arr3.length) // 10
console.log(arr4.length) // 10

一个大胆的猜想

emoji表情判断,可以通过字符串长度和字符数量的比较判断是否存在emoji表情,当长度和数量不一致的时候,存在emoji表情。

function isEmojiStr(str) { 
    if (typeof (str) === 'string') {
        const arr = Array.from(str); 
        if (str.length !== arr.length) { 
            return true; 
        }
    }
    return false; 
}
  • 控制台验证
console.log(isEmojiStr('欢迎来到王者荣耀😊😊'))

console.log(isEmojiStr('🐱‍🐉'))

console.log(isEmojiStr('欢迎来到王者荣耀'))

console.log(isEmojiStr('大家好@!¥%……&*(——+'))

console.log(isEmojiStr('ヾ(≧▽≦*)oφ(* ̄0 ̄)'))

console.log(isEmojiStr('₩㎝㎜㈤㈧㈦'))

console.log(isEmojiStr('❎'))

console.log(isEmojiStr('🚀'))

console.log(isEmojiStr('🅰'))

console.log(isEmojiStr('©'))

image.png

上图中❎、© 是utf8编码的MySQL支持的字符。

参考

# Emoji Unicode Tables
# 深入理解Emoji(一) —— 字符集,字符集编码
# 深入理解Emoji(二) —— 字节序和BOM
# 深入理解Emoji(三) —— Emoji详解