本文来自微信公众号: 宇众不同的露萱 ,作者:宇众不同的露萱,原文标题:《为什么 Emoji 来自日本?》
大概在七八年前,帮朋友研究一个海外社交产品的字符解析模块。
当时有一个很奇怪的在线Bug:部分安卓用户在聊天界面发了一个“💩”(便便),到了iOS端直接变成了一串乱码方框;而如果从iOS发给安卓,对方收到的是一个长得完全不一样的表情,甚至有些老设备上直接丢包了。
排查到最后,我发现这不是接口传输的问题,也不是前端渲染的错误,而是字符集编码的历史债务。
在深入翻看Unicode规范和当年几份提议草案(Proposal)之后,我才意识到,今天全世界几十亿人天天在敲的Emoji,根本不是某个硅谷巨头凭空发明出来的“现代产品”。
它是一段非常离奇的通信演进史。
如果你问大多数人:“为什么Emoji诞生在日本?”
很多人可能会跟你扯日本的漫画文化、可爱文化(Kawaii)、或者是日本人的表达含蓄。
但如果从工程和通信的角度去看,你会发现答案完全不同。
Emoji能够诞生并统治世界,纯粹是因为日本在90年代末极其特殊的移动网络资费结构,以及三家互不兼容的运营商之间,一场关于“私有扩展字符”的冷战。
12秒的计费墙,与一个红色的心形符号
要理解Emoji,得先回到1990年代中期的日本。
当时日本的PHS(个人手持电话系统,也就是中国后来“小灵通”的前身)非常流行。1995年,日本电信公司Pocket Bells推出了面向年轻人的寻呼机,他们在键盘里内置了一个极其小巧的符号——“♥”。
这个简单的爱心符号在当时引发了现象级的轰动。那时候高中生用寻呼机发文本,因为按键繁琐、费用贵,一个简单的爱心不仅能省下好几个字的空间,还能极其精准地传达情绪。
后来Pocket Bells在推出新版寻呼机时,以为年轻人更想要字母和复杂功能,把这个“♥”符号给删了,结果导致大量的年轻用户瞬间流失,直接转投了竞争对手的怀抱。
这件事给日本移动通信界带来了一个非常深刻的震撼:原来在移动通信里,非文本的图形符号,是具备极高商业价值和黏性的。
到了1999年,日本移动运营商巨头NTT DoCoMo正准备推出一个划时代的服务——i-mode。
i-mode是世界上最早的移动互联网雏形之一。你可以在那台黑白或者简陋彩屏的手机上看天气预报、查新闻、发邮件。
但当时的硬件限制极其残酷。
那时候的手机内存以KB为单位,屏幕像素低得可怜。最要命的是资费:i-mode是按数据包(Packet)计费的,每一封邮件的字数上限非常低。或者说,多写几个字,用户要付的日元就会直线上升。
当时在NTT DoCoMo参与i-mode开发的栗田穣崇(Shigetaka Kurita)发现了一个问题:
日本人的书信文化非常讲究客套和礼貌。在纸质信件里,开篇要问候天气,结尾要有敬语。但在手机屏幕上,如果为了省钱把这些客套话全删掉,只留下干瘪的一句话,比如“今天晚饭吃什么?”,在接收者看来就会显得极其冷漠甚至生硬。
如何在极短的字符限制内,把语气、上下文和情绪一次性塞进去?
栗田穣崇想到了用12×12像素的网格去画图。
他拿起了纸和笔,绘制了第一批176个12×12像素的图标,包括天气、心形、星座、交通工具,以及几个简单的脸部表情。

这些图标,就是“絵文字”(Emoji)的起点。日语里的“絵”(E)就是图画,“文字”(Moji)就是字符。
所以Emoji的本质,从一开始就不是“图片”,而是一种为了在极度匮乏的数据管道里传递情绪而发明的“压缩算法”。
NTT DoCoMo的Emoji推出之后,瞬间打爆了市场。
竞争对手KDDI和SoftBank(软银)看在眼里,急在心里。既然你DoCoMo有表情,那我们也必须有。
于是,KDDI和SoftBank迅速推出了自己的Emoji字符集。
但这里埋下了一个极其灾难性的工程隐患:这三家运营商在设计Emoji时,根本没有坐下来商量过统一的编码标准。
在计算机字符集里,无论是ASCII还是Shift-JIS(当时日本常用的汉字编码),都有一块没有被官方定义的空白区域,叫做Private Use Area(PUA,私有保留区)。
标准组织的意思是:这块区域留给你们公司自己玩,比如你自己想搞个特殊的公司Logo字体,可以在内部用,但千万别传到公网上去。
这三家日本运营商,直接把各自的Emoji塞进了Shift-JIS的PUA区域里:
NTT DoCoMo的“马”在编码0xF97B;
KDDI的“马”在编码0xE5A3;
SoftBank的“马”在编码0xF68B。
更可怕的是,不仅编码不一致,连含义都对不上。
DoCoMo里的某个编码代表“开心”,到了SoftBank的相同编码区,可能映射的是“一把枪”或者“一顶帽子”。
这就导致了当年日本移动通信史上著名的“表情乱码危机”:
如果一个DoCoMo用户给SoftBank用户发了一封带有表情的邮件,接收方的手机就会按照自己家运营商的映射表去渲染。
你本想给对方发一个“甜品”,对方手机上弹出来的可能是一个“墓碑”。
到了2000年代中期,日本的移动互联网已经非常发达,但三家运营商之间为了这几百个小图标,在后台写了极其庞大且恶心的映射转化表(Transcoding Tables)。每当用户跨网发短信,邮件网关就要去查找这些硬编码的映射规则,做一次“字符翻译”。
这种私有协议冷战,如果一直留在日本本土,Emoji也许永远只是日本移动通信史上的一个局部特产。
直到两个硅谷巨头准备进入日本市场。
2007年,苹果发布了第一代iPhone。
2008年,Steve Jobs带着iPhone进军日本市场,与日本软银(SoftBank)达成独家合作协议。
当时的软银CEO孙正义给乔布斯提了一个非常硬性的要求:iPhone如果想在日本卖得动,必须支持Emoji。如果不支持Emoji,日本的年轻人根本不会买单。
乔布斯一开始对这件事情完全不感兴趣。对于一个崇尚工业设计和干净排版的硅谷公司来说,这些来自日本运营商的、像素粗糙的小图案,简直就是字符集的异端。
但市场是残酷的。苹果最终妥协了。
iOS 2.2版本里,苹果专门针对日本市场的iPhone秘密内置了一套Emoji键盘。
苹果直接采用了SoftBank的私有PUA编码。这意味着,如果一个美国用户拿到了日本版iPhone,他根本找不到这个键盘;就算通过改配置文件把表情发给普通电脑,对方看到的也是一个未知的空白方框。
几乎在同一时间,谷歌也在准备把Android推向全球,并且他们也想拿下日本市场。
当时在谷歌工作的两个工程师——Mark Davis(也是Unicode联盟的联合创始人)和Yasuo Kida,做出了一个决定:
我们不能再这么修修补补下去了。如果不想让全世界的通信接口被这几百个私有符号搞崩溃,就必须把Emoji正式引入Unicode标准。
2007年,谷歌和苹果联合向Unicode联盟提交了一份长达几十页的提案,正式申请将日本的Emoji纳入Unicode全球通用字符集。
这份提案在当时的Unicode委员会内部引发了极其激烈的争议。
Unicode的最初宗旨是什么?是“为世界上每一种语言的每一个字符提供一个统一的数字编码”。它的本质是语言学和字形学,收录的是汉字、希腊字母、阿拉伯文、梵文。
许多保守派专家认为:
“Unicode是用来传承人类文明和书面语言的。你们居然想把‘💩’(便便)、‘🍣’(寿司)这种商业公司搞出来的低俗小玩艺,和世界上最古老的文字放在同一个编码表里?”
这种反对声音非常大。
但提案者提出了一个无法反驳的工程事实:
Emoji在日本已经被数千万人使用了十几年,它已经成为了现实中真实存在的“数据流”。如果不把它标准化,全世界的软件工程师就要被迫在自己的代码里写一辈子私有映射表。
Unicode最终向现实低头了。
2010年,Unicode 6.0版本发布,正式收录了1000多个Emoji字符。
从此,Emoji彻底摆脱了“日本运营商私有扩展”的身份,变成了和字母A、汉字“国”平起平坐的全球通用一等公民字符。
这也是为什么今天你在任何一台手机上,不管装的是Windows、macOS、Android还是Linux,敲下一个Emoji,它的底层都是一个全局唯一的Unicode码点(比如“💩”的码点就是U+1F4A9)。
为什么那些奇怪的表情至今还在?
当你打开今天的iPhone或者Android键盘,翻到表情页时,你可能会觉得有些奇怪:
为什么会有“🎴”(花牌)、“🏯”(日本城堡)、“🍢”(关东煮)、“🎍”(门松)、“♨️”(温泉符号)、“🙇”(土下座/弯腰道歉)?
这些带有极强日本地域色彩的符号,为什么会堂而皇之出现在一个面向全球几十亿人的通用字符集里?
答案就在刚才讲的历史里:
因为Unicode联盟在收录Emoji时,为了保证向前兼容(Backward Compatibility),遵循了一个冷酷的工程原则:必须无缝兼容1999年日本三家运营商(DoCoMo、KDDI、SoftBank)已经存在的所有历史符号。
哪怕有些符号在欧美甚至亚洲其他国家的人看来极其莫名其妙,但为了不破坏当年日本用户存下的旧邮件和旧数据库,Unicode选择把它们原封不动地全盘接纳。
这就是软件工程里典型的“历史包袱变成全球标准”。
留下来的复杂性
把图形变成了字符,看似优雅地解决了统一编码的问题,但它也给今天的软件工程师留下了极其庞大的复杂性。
很多没有接触过底层的开发者,最容易踩的一个坑就是字符串长度计算。
在以前,一个字符就是1个字节(ASCII)或者2-4个字节(UTF-8)。
但今天的Emoji已经引入了ZWJ(Zero Width Joiner,零宽连字符U+200D)机制。
比如,一个“👩👩👧👦”(由两个女人、一个女孩、一个男孩组成的家庭表情),它在底层根本不是一个字符,而是:
女人`+`ZWJ`+`女人`+`ZWJ`+`女孩`+`ZWJ`+`男孩
它是由7个Unicode码点组合而成的!
如果你在代码里用普通的string.length去计算它的长度,系统可能会告诉你这个看似单一的表情“长度是11”。
如果你在数据库里给用户名限制了VARCHAR(10),用户一旦把这个家庭Emoji填进名字里,系统直接就会报截断错误或者崩溃。
从1995年寻呼机上的那个“♥”,到今天需要靠复杂的渲染引擎和状态机才能解析的组合Emoji,技术一步一步演进到了今天。
