Emoji 与特殊符号

按分类或关键词找 emoji 和特殊符号,点一下直接复制,同时给出它的 Unicode 码点和 HTML 实体。

显示 676 / 676 个符号|emoji 322 个、特殊符号 354 个

字形不由字符决定,由系统决定

这一页最容易让人误会的一点是:emoji 的字形不存在于字符里。Unicode 只管给每个字符分配一个码点,说明它代表「哭笑不得的脸」还是「举手」,至于这个脸长什么样、用什么颜色画,全部交给操作系统自带的彩色字体。苹果、谷歌、微软、三星各画各的,于是同一个 😂 在不同手机上连眼泪的方向都可能不同;同一个 👋 的手势,有的偏暖有的偏冷。网页没法指定用哪家的字形,写字体名也只在装了对应字体的设备上生效,所以在别人的设备上看到不一样的样子是正常的。

比样子不同更麻烦的是「根本没有」。系统字体里缺一个字符时,浏览器会画一个空心方块,行话叫豆腐块。最常见的原因是系统版本太老、Linux 上没装彩色字体、或者用的是精简过的系统镜像。碰到豆腐块没有补救办法,只能换一个收录更早的符号。这份清单尽量只收十多年前就标准化的 emoji,就是为了让复制走的字符更大概率在对方那边画得出来。

组合序列:一个看起来的符号其实是好几个码点

国旗、家庭、职业、带肤色的手势,看起来是一个符号,实际是一串码点靠规则拼出来的。国旗是两个「区域指示符号」相连:🇨🇳 的背后是 U+1F1E8 和 U+1F1F3。家庭和职业序列则用「零宽连接符」U+200D 把几个 emoji 粘起来,比如程序员就是 🧑 加 ZWJ 加 💻。肤色靠的是 U+1F3FB 到 U+1F3FF 这五个修饰符,跟在基础符号后面改写肤色。这些规则都要系统主动支持:认得出的系统合成一张图,认不出的系统就老老实实把每个码点单独画出来,于是家庭 emoji 在旧手机上变成了三个独立的人。

还有一个看不见的字符是变体选择符 U+FE0F。有些码点天生有两种画法,符号 ❤ 默认是黑白文字样式,加上 U+FE0F 之后就变成彩色。它本身不占显示位置,但真实存在于字符串里,这也是同一条文本复制来复制去、长度却对不上、甚至后续文字颜色莫名改变的原因之一。页面上选中一个符号时会把它的每个码点单独列出来,单字符和组合序列一眼就能区分。

码点、实体与长度

Unicode 码点是字符的唯一编号,写成 U+1F600 这种形式,它和 HTML、和操作系统都无关,Python、Java、Rust 的源码里也用它引用字符。HTML 实体是「在 HTML 里表示这个字符」的写法:十进制实体是「和号、井号、十进制编号、分号」,十六进制实体把编号换成 16 进制并在井号后加个 x。实体只在 HTML 和 XML 里有意义,放进别的语言、纯文本或者聊天框,会被原样显示成那串符号,这也是搜「HTML 实体」来找字符通常不如直接粘字符好用的原因。

长度是另一个坑。JavaScript 的字符串按 UTF-16 编码,基本平面之外的字符每个要占两个码元,所以一个 emoji 在字数统计里算两个字;带肤色或 ZWJ 的组合序列能占到七八个。很多表单和游戏按码元限长,用 emoji 顶字数很容易触发校验失败。要统计真实字符数,去看字数统计里按码点统计的那一栏;要把字符在码点和实际字节之间来回看,用Unicode 转义和十六进制转文本;要处理实体就配HTML 实体转义。键盘上直接能打出来的符号,用键盘符号英文对照查 Alt 代码和 Mac 组合键会更顺手。

常见问题

为什么同一个 emoji 在我和朋友手机上长得不一样?

因为 emoji 的字形不存在于字符本身,而是由操作系统自带的那套彩色字体画出来的。现在的 emoji 标准只管「这个码点表示什么概念」,不管「它长什么样」,所以苹果画的是苹果风格、谷歌画的是谷歌风格、微软画的是微软风格——同一个 😂 在三种系统上可能连眼泪的方向都不一样。网页没法指定用哪一家的字形,CSS 里写字体名也只能在装了对应字体的设备上生效,所以在别的设备上看到不同样子是正常的,不是你的页面出了错。

为什么有的 emoji 显示成一个小方块?

那个方块叫豆腐块,意思是当前系统的字体里根本没有这个字符的字形。最常出现在三种情况:系统版本太老,还没收录这个码点;Linux 上没装彩色 emoji 字体;或者你用的是精简过的系统镜像。碰到豆腐块没有补救办法,只能换一个系统支持更久的符号,或者干脆用文字描述。这也是这份清单尽量只收老 emoji 的原因。

国旗为什么在 Windows 上变成了 CN 两个字母?

国旗不是单独的字符,而是两个「区域指示符号」拼出来的:🇨🇳 其实是 U+1F1E8 和 U+1F1F3 两个码点连在一起。支持这套规则的系统会把它们渲染成一面旗,Windows 则不支持地区旗帜的合成,就老老实实把两个区域符号画成了带框的字母 C 和 N。所以同一个纸条在 Mac 上是旗子、在 Windows 上是字母,代码里最好别把国旗当成唯一的国家标识。

肤色 🏻 是怎么加上去的?

靠「肤色修饰符」,一共五个码点:U+1F3FB 到 U+1F3FF,从浅到深。它们紧跟在 👍、🙋 这类支持肤色的基础符号后面,系统一看后面跟了修饰符,就把基础符号换成对应肤色的版本。要点是:修饰符不能单独使用,跟在随便一个字符后面会显示成一个独立的小方块;而且不是所有人物符号都支持,跟着不支持的符号时,有的系统会直接把它丢掉。

为什么 👨‍👩‍👧 这种家庭 emoji 在旧系统上会拆成好几个人?

因为它不是一个字符,而是好几个 emoji 用「零宽连接符」U+200D 串起来的序列:男人 + ZWJ + 女人 + ZWJ + 女孩。新系统认这个序列,会合成一张图;旧系统不认识,就按 ZWJ 前后的内容逐个画,于是你看到三个独立的人。同理还有程序员 🧑‍💻、医生这类职业序列。页面上点开一个符号时会把它的每个码点都列出来,一眼就能看出它是单字符还是拼出来的。

那个看不见的 U+FE0F 是干什么的?

它是「变体选择符 16」,作用是让前面的字符以彩色 emoji 的样子显示。有些码点本来就有两种画法:比如 ❤ 默认是黑白文字样式(U+2764),加上 U+FE0F 之后才变成彩色的 ❤️。这也是为什么复制的时候会莫名多出一个看不见的字符,甚至改变后续文字的颜色。做校验或者比对字符串时,这类不可见字符最容易造成「看起来一模一样却不相等」。

码点和 HTML 实体有什么区别,该用哪个?

码点是这个字符在 Unicode 里的编号,写成 U+1F600 这种形式,和 HTML 无关,写代码、查字符都用它。HTML 实体是网页里表示字符的写法,十进制是「和号、井号、编号、分号」,十六进制是把编号换成 16 进制并在井号后加个 x。放进 HTML 或 XML 里实体最稳,放在别的语言、纯文本或者聊天框里就直接写字符本身,写实体则会被原样显示成那串符号。

为什么复制到游戏、昵称或者表单里字数就不对了?

因为这类字符在 JS 和很多后端里是按 UTF-16 码元数的。一个超出基本平面的 emoji 占两个码元,所以「一个符号」在字数限制里算两个字;带肤色或 ZWJ 的组合序列能占到七八个。页面上选中符号时会同时给出码点个数和真正的字节开销参考,但最终以目标平台为准。另外游戏、银行、老式表单常常直接过滤非 ASCII 字符,这类地方不要用它来绕长度限制,校验失败通常是整条输入被拒。

相关:Unicode 转义HTML 实体转义键盘符号英文对照