http://blog.csdn.net/binjly/article/details/47321043 javascript
今天,測試給我提了一個BUG,說移動端輸入emoji表情沒法提交。很早之前就有思考過,手機輸入法裏自帶的emoji表情,應該是某些特殊字符。既然是字符,那應該都能提交纔對,但是爲啥會被卡住呢?搜了一下,才發現,原來emoji用到的字符是4字節的utf-16(utf-16有2字節和4字節兩種編碼),而咱們的數據庫是採用的utf-8,而且最大隻容許3字節的字符。這樣衝突就產生了,表單由於這些emoji字符的存在沒法提交。html
找到緣由以後,接下來就要考慮解決方案了。目前考慮到的兩種方案,一是讓後臺處理,把這個utf-16字符作一些轉換(這裏不作討論)。第二種辦法就是在前端直接轉換成實體字符後再提交。這樣,後臺不用作任何處理,用戶的提交的信息也得以保留,是否是一個一箭雙鵰的辦法呢?接下來咱們要討論的就是怎麼把emoji表情字符轉換成實體字符。前端
首先,咱們來看看手機輸入法裏自帶的emoji字符是什麼樣。下面截了一張圖,來自 http://computerism.ru/emoji-smiles.htm 。咱們看到,每一個emoji表情字符對應的實體字符編碼都比較大,如第一行的笑臉,實體字符爲😊 。並且,咱們注意到,後面還有一個16進制的編碼 D83DDE0A。那這個編碼是幹嗎用的?接着往下看。java
1、字符檢測正則表達式
要想把這些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,是否是忽然就明白了呢?數據庫
2、轉換算法函數
如今,咱們已經可以檢測出表單裏的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)的公式
但是,這個是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的餘,那麼就有了如下公式:
公式出來以後,相信你們已經知道怎麼作了,不過最後仍是獻一下醜,把我本身寫的一個處理函數提供給你們參考:
運行結果以下:
細心的童鞋,在剛剛看那些參考文章的時候,也許已經發現了,其實並非全部的emoji表情字符都是utf-16編碼的,也有一部分落在了ucs-2編碼的範圍(即只用了兩個字節)。不過這都不是重點,重點是,咱們已經成功的把utf-16編碼部分的emoji表情轉換爲了實體字符。