在某个 iOS 项目中,需要一个服务器来保存一些用户数据,例如用户信息、评论等,我们的服务器端使用了 PHP+MySQL 的搭配。在测试过程中我们发现,用户在 iOS 端里输入了 Emoji 表情提交到服务器以后,PHP 无法在 MySQL 数据库里正确保存,会遇到乱码的问题。下面是原因探析和解决办法。
说起 Emoji 的历史说开去那可谓是源远流长了,这里简要介绍 Emoji 在 iOS 系统中的变迁史。不知道何时起,日本人喜欢发 Emoji 表情图标,于是日本的运营商 DoCoMo、KDDI、SoftBank 推出了自己的 Emoji 字符集。在这些字符集中,一个 Emoji 表情的实际上是一个 UTF8 字符,这点不同于 QQ 或者微信的表情,用户发短信的时候一个 Emoji “图像”只占用了一个字符,那是相当的方便。苹果的 iOS 和 Mac OS X 系统内置了 Apple Color Emoji(Apple 彩色表情符号)的字体用来显示 Emoji 表情,iOS 里有一个 Emoji 输入键盘,可以输入各种 Emoji 表情图标,在 Mac OS X 里使用 Option + Command + T 调出输入键盘里选择“表情符号”输入,Mac OS X 里的字体文件位于 /System/Library/Fonts/Apple Color Emoji.ttf
。抛开版权因素,把这个文件拷贝到 Windows 的字库文件夹里,Windows 也能支持 Emoji 的显示了。
从技术上说,单个 Emoji 表情实际上是使用 UTF8 编码的一个字符,苹果在 iOS4 开始支持了 Emoji 的显示,当时用的是 SoftBank 的方案。在日本的三家运营商之间,用来表示同一个表情的 UTF8 码并不相同(见对比列表),他们之间要做一个转换才能正确显示。以相同一个表情笑脸为例,SoftBank 表示为 U+E04A
,KDDI 则表示为 U+E488
。
不过唯一的好处在于,这时候的每个 Emoji 图标,都是使用 3 字节的 UTF8 字符编码。说句题外话,在刚开始 PHP 开发的时候,大家都知道一个中文在 UTF8 字符集占据 3 个字节。 回到 iOS 和 Web 服务器之间的通信方案,我们可以直接把 iOS 中用户输入的 Emoji 表情,通过 PHP 存入 MySQL 数据库中,如果在 iOS 中展示,直接把数据传递给 iOS,客户端应该就能正确展示表情图标了,如果把用户在 iOS 端输入的表情在 Web 端展示,需要把 Emoji 字符转化为对应的图片(当然支持 Emoji 的 Mac OS X 系统可以通过指定网页 CSS 的 font-family
属性直接显示)。Emoji for PHP 组件能够方便地在各种 Unified、DoCoMo、KDDI、SoftBank、Google 编码之间进行转换。
从 iOS5 开始,苹果开始支持标准的 Unified 编码的 Emoji 表情。这其中最大的优点莫过于统一了规格,方便了用户和开发者。但是,Web 开发者在开发网页端的时候就会遇到一些问题,以往直接把 Emoji 表情存储在数据库中现在则会导致乱码。这是由于采用 Unified 标准的单个 Emoji 字符一般为 4 个字节,超过了 MySQL 数据库 utf8 字符集支持的每个字符 3 个字节的限制,存入数据库以后会产生乱码或者破坏数据。
如果说解决该问题,最方便的做法是在存入数据库之前,把每个 Emoji 字符转化为 UBB 代码,或者是其他可辨认的形式,如 HTML 转义字符。还是以太阳符号为例,它的 Unified Emoji 编码为 U+2600
,在存入数据库时,可以把它转换成 UBB 代码 [emoji]2600[/emoji]
保存,也可以使用 HTML 转义字符 ☀
储存。如果在 Web 前端展示,用 HTML 转义字符可以直接输出,用 UBB 代码则方便论坛等需要严格安全验证用户输入的地方使用。
还有一种做法你可能也猜到了,采用 SoftBank 方案的单个 Emoji 表情占用 3 个字节,可以直接保存在数据库里,所以可以把 Unified Emoji 转化为 SoftBank Emoji 格式,存储在数据库中就不会有任何问题了。
另外一种方式从 MySQL 自身解决。从 MySQL 5.5.3 开始,MySQL 支持一种 utf8mb4 的字符集,这个字符集能够支持 4 字节的 UTF8 编码的字符。 utf8mb4 字符集能够完美地向下兼容 utf8 字符串。在数据存储方面,当一个普通中文字符存入数据库时仍然占用 3 个字节,在存入一个 Unified Emoji 表情的时候,它会自动占用 4 个字节。所以在输入输出时都不会存在乱码的问题了。要使用 MySQL 的这个特性,首先需要把 MySQL 升级到 5.5.3 以上的版本。其次,需要修改数据结构中的字符集为 utf8mb4 ,如 utf8mb4_general_ci
。由于 utf8mb4 是 utf8 的超集,从 utf8 升级到 utf8mb4 不会有任何问题,直接升级即可;如果从别的字符集如 gb2312 或者 gbk 转化而来,一定要先备份数据库。然后,修改 MySQL 的配置文件 /etc/my.cnf
,修改连接默认字符集为 utf8mb4 ,如果是自己写的 PHP 脚本,也可以在连接数据库以后首先执行一句 SQL: SET NAMES utf8mb4;
。这时候,PHP 应该就可以正常保存 Emoji 到数据库了。
苹果在 iOS6 和 Mac OS X 10.8 中又增加了 300 多个 Emoji 表情,而 SoftBank 编码因为渐渐被历史淘汰,已经没有了相应的升级,所以上文提到的把 Unified Emoji 转化为 SoftBank Emoji 后保存在数据库中的方案已经落伍了。
说了那么多,最终还是要归纳总结一下解决方法。
从数据库中保存的形态出发,要么用 UBB 或者 HTML 转义字符等替代方法保存,要么用数据库直接存储 Emoji 字符。如果直接保存 Emoji 字符,则必须将 MySQL 升级到 5.5.3 以上并且采用 utf8mb4 字符集。如果用替代方法,则不用升级数据库,不影响现有的服务器,是比较省事的方法。
在用户输入方面,以某著名门户新闻客户端为例,常见的几个平台包括:Web,iOS,安卓等。 Web 端上主要是让用户通过点击图标输入 Emoji 表情,在文本框里可以直接以 UBB 代码[emoji]2600[/emoji]
的形式显示,也要考虑 Mac OS X 或 iOS 移动网页端用户直接通过键盘输入 Emoji 字符,这时候最好在 CSS 里添加 textarea{font-family:"Apple Color Emoji","Arial";}
,以便 Mac OS X 上的 Chrome / Firefox 浏览器用户输入了表情以后能直接显示出来,而不是黑框框。iOS 客户端不必说了,直接让用户使用 iOS 自带的表情键盘即可。至于 Android, Windows Phone 或者黑莓等其他平台,一般要通过第三方的输入法来输入 Emoji 表情。至于用户输入的 Emoji 字符还是 UBB 代码,服务器端应该都要有处理的能力。值得注意的是,可能 SoftBank 等 Emoji 字符集都已经不是主流了,在中国的用户估计很少,但如果你想做一个像微信那么跨国界的 APP,还是不妨加一个转换,首先将各种形式的 Emoji 字符首先转换为 Unified Emoji。
在 Web 端显示方面,出于各平台的兼容性考虑,把 Emoji 表情通过图片输出是最合适的做法。无论在数据库中我们保存的形态是什么,把它转化为图片都需要一个对应表。Emoji for PHP已经实现了 iOS5 的 500 多个表情的支持,但尚未提供 iOS6 新增的 300 多个表情。我虽然已经做了对应关系,但是因为没有整理出代码,暂时没办法放出来。
在 iOS 端显示方面,鉴于用户普遍升级到 iOS5 以上,自然是显示原生支持的 Unified Emoji。在安卓和其他移动平台客户端上,我暂时没有研究过,但估计显示图片是比较好的选择。
参考资料: