http://blog.csdn.net/binjly/article/details/47321043  

  今天,测试给我提了一个BUG,说移动端输入emoji表情无法提交。很早以前就有思考过,手机输入法里自带的emoji表情,应该是某些特殊字符。既然是字符,那应该都能提交才对,可是为啥会被卡住呢?搜了一下,才发现,原来emoji用到的字符是4字节的utf-16(utf-16有2字节和4字节两种编码),而我们的数据库是采用的utf-8,并且最大只允许3字节的字符。这样冲突就产生了,表单因为这些emoji字符的存在无法提交。

  找到原因之后,接下来就要考虑解决方案了。目前考虑到的两种方案,一是让后台处理,把这个utf-16字符做一些转换(这里不做讨论)。第二种办法就是在前端直接转换成实体字符后再提交。这样,后台不用做任何处理,用户的提交的信息也得以保留,是不是一个两全其美的办法呢?接下来我们要讨论的就是怎么把emoji表情字符转换成实体字符。

  首先,我们来看看手机输入法里自带的emoji字符是什么样。下面截了一张图,来自 http://computerism.ru/emoji-smiles.htm 。我们看到,每个emoji表情字符对应的实体字符编码都比较大,如第一行的笑脸,实体字符为😊 。而且,我们注意到,后面还有一个16进制的编码 D83DDE0A。那这个编码是干嘛用的?接着往下看。

一、字符检测

  要想把这些emoji表情字符转换成实体字符,那么就要先把它们检测出来。说到字符检测,我们的正则这时就该上场了。首先我们得确定这些字符的范围。前面我们已经知道,emoji表情字符用的是4字节的utf-16编码,而4字节的utf-16编码不被后台接受。所以,我们的检测范围就变成了把所有4字节的utf-16编码检测出来。我们通过搜索查到,4字节的utf-16编码范围为U+010000到U+10FFFF,那么,我们的正则是不是可以这么写:/[\u010000-\u10FFFF]/g ? NO,你会发现这个正则完全不能按我们预期工作。这是为什么呢?

  上面这个问题,一些童鞋可能已经知道答案了。没错,就是javascript的编码问题引起的。我们知道,javascript采用的是unicode编码,再准确一点说,是ucs-2编码。从名字上,我们就已经知道,这种编码方案是2字节的。在2字节的编码中找4字节的字符,很显然并没那么简单。所以,我们得考虑一下,这个utf-16在ucs-2编码中是如何表示的呢?这里,我搜到了我们可爱的传教士——阮老师的一篇文章 《Unicode与JavaScript详解》(http://www.ruanyifeng.com/blog/2014/12/unicode.html) 。 简单来说,就是把utf-16的4字节字符,拆分成两个ucs-2的2字节字符。具体算法可参考阮老师的上述文章,本文就不详细讨论了。从阮老师的文章中,我们已经知道了,4字节utf-16在js中被用两个字符来表示,高位范围为0xD800 - 0xDBFF,低位范围为0xDC00 - 0xDFFF。那么我们用于检测的正则表达式也就出来了:/[\uD800-\uDBFF][\uDC00-\uDFFF]/g 。现在再回过头看看我们第一张图的那串16进制,D83DDE0A、D83DDE03,是不是突然就明白了呢?

二、转换算法

  现在,我们已经能够检测出表单里的emoji表情字符。那么,重头戏来了,我们怎么把这个字符转换成实体字符呢?我们知道,实体字符是用来表示单个字符的编码,而我们的emoji表情,在js里,却是用两个字符来表示的。这可怎么办?等等,谁说emoji是两个字符,说好的4字节单字符呢?没错,一开始emoji就是用utf-16表示的啊 这里,我又参考了另一篇文章,http://unicode-table.com/cn/sets/emoji/,以下截了一部分图以做说明。

  我们还是以那个笑脸的字符为例,其utf-16编码为U+1F600,我们转成十进制看看。

  128512不正好是我们的实体编码😀 吗?所以,现在问题又变成了怎么取得emoji表情字符utf-16编码的问题了。可是,可是,我们刚刚已经知道了,在js里,emoji表情也是用ucs-2编码的啊,只不过变成了用两个字符来表示。那么,我们的问题最终演变成了怎么从ucs-2编码转换成utf-16编码的问题。

  感谢阮老师,在阮老师的那篇文章中,有提到utf-16转ucs-2(unicode)的公式

  1. H = Math.floor((c-0x10000) / 0x400)+0xD800 // 高位
  2. L = (c - 0x10000) % 0x400 + 0xDC00 // 低位

  可是,这个是utf-16转ucs-2,我们要的是ucs-2转utf-16啊?怎么办?推导回去呗。我们先看看这两个公式都做了什么。首先,高位的公式,把字符C减0x10000,再除0x400,取其商,再加0xD800。而低位则是字符C减0x1000,取除0x400的余,再加0xDC00。所以这个字符其实被分成了两部分:商和余,然后再把处理后的商做为高位,加上处理后的余做为低位,这样组合成了ucs-2字符。我们知道,被除数=除数×商+余数。那么,我们也可以反过来,求得C/0x400的商,再加上C/0x400的余,不就能算出C了吗。为了便于计算,我们用Q表示C的商,用M表示C的余,那么就有了以下公式:

  1. H = Q - 0x10000 / 0x400 + 0xD800
  2. L = M - 0x10000 % 0x400 + 0xDC00
  3. C = Q * 0x400 + M
  4. // 因为0x10000 % 0x400 = 0,故推得:
  5. H = Q - 0x10000 / 0x400 + 0xD800
  6. L = M + 0xDC00
  7. C = Q * 0x400 + M
  8. // 根据C的公式,把H*0x400再加L,得到:
  9. H * 0x400 + L = Q * 0x400 - 0x10000 / 0x400 * 0x400 + 0xD800 * 0x400 + M + 0xDC00
  10. // 最后把Q * 0x400 + M换成C,得到:
  11. H * 0x400 + L = C - 0x10000 + 0xD800 * 0x400 + 0xDC00
  12. // 移项后,我们最终的公式为:
  13. C = (H - 0xD800) * 0x400 + 0x10000 + L - 0xDC00

  公式出来之后,相信大家已经知道怎么做了,不过最后还是献一下丑,把我自己写的一个处理函数提供给大家参考:

  1. /**
  2. * 用于把用utf16编码的字符转换成实体字符,以供后台存储
  3. * @param  {string} str 将要转换的字符串,其中含有utf16字符将被自动检出
  4. * @return {string}     转换后的字符串,utf16字符将被转换成&#xxxx;形式的实体字符
  5. */
  6. function utf16toEntities(str) {
  7. var patt=/[\ud800-\udbff][\udc00-\udfff]/g; // 检测utf16字符正则
  8. str = str.replace(patt, function(char){
  9. var H, L, code;
  10. if (char.length===2) {
  11. H = char.charCodeAt(0); // 取出高位
  12. L = char.charCodeAt(1); // 取出低位
  13. code = (H - 0xD800) * 0x400 + 0x10000 + L - 0xDC00; // 转换算法
  14. return "&#" + code + ";";
  15. } else {
  16. return char;
  17. }
  18. });
  19. return str;
  20. }

  运行结果如下:

  细心的童鞋,在刚刚看那些参考文章的时候,也许已经发现了,其实并不是所有的emoji表情字符都是utf-16编码的,也有一部分落在了ucs-2编码的范围(即只用了两个字节)。不过这都不是重点,重点是,我们已经成功的把utf-16编码部分的emoji表情转换为了实体字符。

【转】移动前端手机输入法自带emoji表情字符处理的更多相关文章

  1. 移动前端手机输入法自带emoji表情字符处理

    今天,测试给我提了一个BUG,说移动端输入emoji表情无法提交.很早以前就有思考过,手机输入法里自带的emoji表情,应该是某些特殊字符.既然是字符,那应该都能提交才对,可是为啥会被卡住呢?搜了一下 ...

  2. 【转】【异常处理】Incorrect string value: '\xF0\x90\x8D\x83...' for column... Emoji表情字符过滤的Java实现

    http://blog.csdn.net/shootyou/article/details/44852639 Emoji表情字符现在在APP已经广泛支持了.但是MySQL的UTF8编码对Emoji字符 ...

  3. PHP实现识别带emoji表情的字符串

    function have_special_char($str) { $length = mb_strlen($str); $array = []; for ($i=0; $i<$length; ...

  4. 一个emoji引发的一条血案:mysql存储emoji表情字符时报错解决

    以下是我插入一条带表情的数据到mysql后出现错误 2019-03-04 14:24:40,462 ERROR 2807 [-/139.199.27.244/-/2ms POST /api/activ ...

  5. MYSQL 写入emoji表情字符处理

    这个鬼emoji表情是4个字节,mysql使用的utf8编码,UTF8占3个字节,要存储那个emoji表情需要将mysql编码由UFT8改为UFT8的超集,utf8mb4; 改数据库编码容易引起大面的 ...

  6. 带emoji表情弹出层的评论框,semantic+emoji picker,java.sql.SQLException: Incorrect string value: '\xF0\x9F..'

    在自己做一个项目玩时,在做评论的时候. 选中了semantic.js原型,这个在国内用的不是很多,但是在github上star数量很高,想当初我想找一个js框架是就在上面找的. semantic中文网 ...

  7. 手机自带输入法emoji表情的输入,提交及显示——纯前端解决方案

    很早之前就遇到过需要前端支持用户输入并提交emoji表情的问题,一直没有尝试去解决,今天再一次狭路相逢,该来的躲不过,那就着手解决吧. 大多数emoji表情都是4字节的utf-16编码(辅助平面字符, ...

  8. 手机自带输入法emoji表情的输入,提交及显示——前端解决方案

    体验更优排版请移步原文:http://blog.kwin.wang/programming/emoji-transform-commit.html 之前就遇到过需要前端支持用户输入并提交emoji表情 ...

  9. mysql utf8mb4与emoji表情

    一 什么是Emoji emoji就是表情符号:词义来自日语(えもじ,e-moji,moji在日语中的含义是字符) 表情符号现已普遍应用于手机短信和网络聊天软件. emoji表情符号,在外国的手机短信里 ...

随机推荐

  1. C#创建XML文件并保存

    随着XML的普及以及在动态WEB应用程序中大量应用,如何通过.NET创建,删除,修改XML文件变的也来也重要了.一个简单的概念是,XML文件跟大的文本文件并没有什么区别,同时它是先于.NET出现,很多 ...

  2. Zookeeper安装和配置

    Zookeeper的安装和配置,可以配置成单机模式.伪集群模式.集群模式. 参考http://coolxing.iteye.com/blog/1871009 一. 单机模式 (1)zookeeper下 ...

  3. 通过ngx-lua来统计Nginx上的虚拟主机性能数据

    Web server调研分析 Filed under: Web Server — cmpan @ 2012-10-29 20:38:34 摘要 简单可依赖的架构首先需要有一个简单可依赖的前端WebSe ...

  4. BZOJ3053: The Closest M Points

    题解: 我们可以事先在堆里放入插入m个inf然后不断的比较当前值与堆首元素的大小,如果小于的话进入. 估计函数也可以随便写写... query的时候貌似不用保留dir... return 0写在 wh ...

  5. erlang判断语法结构:if/case/guard

    erlang 有好几种常用的判断结构语句,如 if.case.guard 等.文章将分别对 if / case /guard 的特点做介绍,以及用例说明 1.if 结构 if Condition 1  ...

  6. 【转】经典SQL语句大全

    原博客地址:http://www.cnblogs.com/yubinfeng/archive/2010/11/02/1867386.html (超级纳闷为啥没有转载的功能) 一.基础 1.说明:创建数 ...

  7. java jvm学习笔记三(class文件检验器)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验 ...

  8. hive内部表与外部表区别

    1.在Hive里面创建一个表: hive> create table wyp(id int,    > name string,    > age int,    > tele ...

  9. 【转载】linux命令行计算器bc的一个“坑”

    [转载自]http://blog.chinaunix.net/uid-174325-id-3518953.html 结论:ibase,obase可以使用在不同的计算公式里,但是尽量把obase放iba ...

  10. BNUOJ-15505 Largest Rectangle in a Histogram DP

    题目链接:http://www.bnuoj.com/bnuoj/problem_show.php?pid=15505 每个h[i]维护两个值l[i]和r[i],分别表示大于h[i]的左边最远距离和小于 ...