go语言:彻底掌握emoji

文章详细介绍了emoji的实现方式,包括Unicode、utf8和字符的区别,以及emoji的多种分类,如基本emoji、键帽序列、旗帜序列、标签序列和修饰序列。同时,文中还提到了Go语言处理emoji的方法,通过构建合法序列树来匹配和处理emoji表达式。
摘要由CSDN通过智能技术生成

什么是 emoji

emoji 就是一些意形符号。

emoji 的实现

首先,你必须能够区分 unicode、utf8 和字符之间的区别:

unicode,字符集,就是一个表格,记录这字符和码点(通常表示为 U+0031)之间的关系
utf8,是 unicode 的编码方案之一(还有 utf16、utf32 等)
字符,是人类可以阅读的符号。emoji 就是一批比较特殊的符号(可以理解为图片或者像素点集合)
因为是一种实现,所以不同平台实现的各不一样。以 ok 表情为例,各平台的实现如下:

emoji 分类

emoji-sequences.txt

Basic_Emoji

基本 emoji,包含两种类型

单一 unicode 字符
单一 unicode 字符后面增加 *U+FE0E* 或者 U+FE0F 分别表示以黑白文本模式还是彩色模式展示表情
emoji-variation-sequences.txt)

Emoji_Keycap_Sequence

键帽序列,都是 0-9、#、* 开头,然后后面紧跟着 U+FE0F 和 U+20E3 两个字符组合而成。

字符长度:均是3字符

需要注意的是苹果输入法打出的键帽序列是反的,即 U+20E3 在前 U+FE0F 在后面

func TestEmoji(t *testing.T)  {
    s := "1⃣️"
    fmt.Printf("字节数:%d, 字符数:%d\n", len(s), len([]rune(s)))
    hexDump(s)
}

func hexDump(s string)  {
    fmt.Printf("字符串:%s\n======================\n", s)
    for index, item := range []rune(s) {
        hex := strconv.FormatInt(int64(item), 16)
        fmt.Printf("序号%d:Unicode 码点:U+%s,字节数:%d\n", index, hex, utf8.RuneLen(item))
    }
}

// 字节数:7, 字符数:3
// 字符串:1⃣️
// ======================
// 序号0:Unicode 码点:U+31,字节数:1
// 序号1:Unicode 码点:U+20e3,字节数:3
// 序号2:Unicode 码点:U+fe0f,字节数:3
RGI_Emoji_Flag_Sequence

RGI (Recommended for General Interchange) 表示可以在日常的交流中使用。

如上表所示,U+1F1E6 ~ U+1F1FF,分表代表 A ~ Z 共计 26 个字符。

比如中国缩写是 CN,所以对应的旗帜编码就是 U+1F1E8(C) U+1F1F3(N)
比如美国缩写是 US,所以对应的旗帜编码就是 U+1F1FA(U) U+1F1F8(S)
RGI_Emoji_Tag_Sequence

这里有三个比较特殊的地区,分别是:英格兰、苏格兰和威尔士。

英格兰 (󠁧󠁢󠁥󠁮󠁧🏴󠁧󠁢󠁥󠁮󠁧󠁿):U+1F3F4 U+E0067 U+E0062 U+E0065 U+E006E U+E0067 U+E007F

苏格兰 (🏴󠁧󠁢󠁳󠁣󠁴󠁿):U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 U+E007F

威尔士 (🏴󠁧󠁢󠁷󠁬󠁳󠁿):U+1F3F4 U+E0067 U+E0062 U+E0077 U+E006C U+E0073 U+E007F

他们是英国的组成国,具体请参考这里

RGI_Emoji_Modifier_Sequence

Unicode 定义了 5 个用于 emoji 的肤色修饰字符,即特定的表情加上肤色修饰字符就会展示不同的颜色:

U+1F3FB:light skin tone
U+1F3FC:medium-light skin tone
U+1F3FD:medium skin tone
U+1F3FE:medium-dark skin tone
U+1F3FF:dark skin tone
比如👌的表情其实有 5 种肤色:

零宽度链接符

emoji-zwj-sequences.txt)

ZWJ 即 Zero Width Joiner,也就是零宽度链接符号。ZWJ 的 unicode 代码为 U+200D,因为是没有宽度的链接符,所以不可见,他的作用就是链接两个字符,比如 “👨‍👩‍👧‍👦” 就是由 U+200D 链接四个字符而成:

字符串:👨‍👩‍👧‍👦,占用字节数:25, 字符数:7
======================
序号0:Unicode 码点:U+1f468,字节数:4
序号1:Unicode 码点:U+200d,字节数:3
序号2:Unicode 码点:U+1f469,字节数:4
序号3:Unicode 码点:U+200d,字节数:3
序号4:Unicode 码点:U+1f467,字节数:4
序号5:Unicode 码点:U+200d,字节数:3
序号6:Unicode 码点:U+1f466,字节数:4
当然这样的组合并不是无限的,所有合法的组合都可以在这里找到。

emoji 总结

emoji 字符非固定长度,单个字符占用 3-4 个字符,所以判断是否是 emoji 表情,不能简单通过长度判断。


emoji 符号可以由一个或者多个字符组合而成,比如👨‍👩‍👧‍👦是由 7 个字符组成,共占用 25 个字节。
go 与 emoji

go-xman/go.emoji

原理就是根据 emoji 两个官方文档 Emoji Sequence 和 Emoji ZWJ Sequence,官方已经将可以组合的 emoji 表情一一列举出来了。

代码实现就是将所有合法的序列全部导出成为一棵树。当检查字符串子串的时候,匹配树中所代表的合法的子串就可以了。

func TestEmoji(t *testing.T)  {
    s := "👩‍👩‍👦🇨🇳"
    _ = emoji.ReplaceAllEmojiFunc(s, func(emoji string) string {
        hexDump(emoji)
        return ""
    })
}

func hexDump(s string)  {
    fmt.Printf("字符串:%s,占用字节数:%d, 字符数:%d\n======================\n", s, len(s), len([]rune(s)))
    for index, item := range []rune(s) {
        hex := strconv.FormatInt(int64(item), 16)
        fmt.Printf("序号%d:Unicode 码点:U+%s,字节数:%d\n", index, hex, utf8.RuneLen(item))
    }
}

=== RUN   TestEmoji
字符串:👩‍👩‍👦,占用字节数:18, 字符数:5
======================
序号0:Unicode 码点:U+1f469,字节数:4
序号1:Unicode 码点:U+200d,字节数:3
序号2:Unicode 码点:U+1f469,字节数:4
序号3:Unicode 码点:U+200d,字节数:3
序号4:Unicode 码点:U+1f466,字节数:4
字符串:🇨🇳,占用字节数:8, 字符数:2
======================
序号0:Unicode 码点:U+1f1e8,字节数:4
序号1:Unicode 码点:U+1f1f3,字节数:4
--- PASS: TestEmoji (0.00s)
PASS
参考文献

Unicode
Unicode 颜文字(emoji)格式和 Go 代码处理
About Emoji
繪文字

————————————————
原文作者:nanjingfm
转自链接:https://learnku.com/articles/54955
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值