Emoji表情轉(zhuǎn)UTF-8編解碼、過濾

前言

開發(fā)過程中驼侠,遇到了帶有Emoji表情的字符串無法存入后臺(tái)數(shù)據(jù)庫(kù)問題姿鸿。原因是Mysql的utf8編碼最多3個(gè)字節(jié),而Emoji表情或者某些特殊字符是4個(gè)字節(jié)倒源,所以數(shù)據(jù)插不進(jìn)去苛预。這種問題應(yīng)該交給后臺(tái)處理,如果后臺(tái)不處理笋熬,則ios和Andriod要統(tǒng)一對(duì)emoji表情編碼热某,這里采用UTF-8編碼,在解決這個(gè)問題之前胳螟,先對(duì)以下相關(guān)知識(shí)做個(gè)了解昔馋。

相關(guān)知識(shí)

1.位、字節(jié)糖耸、字符的概念和區(qū)別秘遏。

位(bit): 數(shù)據(jù)存儲(chǔ)的最小單位,每個(gè)二進(jìn)制數(shù)字0或者1就是1個(gè)
位嘉竟,比如 11010101是一個(gè)八位二進(jìn)制數(shù)邦危。
字節(jié)(byte):8個(gè)位構(gòu)成一個(gè)字節(jié);即:1 byte (字節(jié))= 8 bit(位)舍扰;1 KB = 1024 B(字節(jié))倦蚪;
字符: 是指計(jì)算機(jī)中使用的字母、數(shù)字边苹、字和符號(hào)陵且,也就是用戶能看到的。
字符集: 各種各個(gè)字符的集合勾给,也就是某些漢字滩报、字母(A、b播急、c)和符號(hào)(空格脓钾、引號(hào)..)會(huì)被收入標(biāo)準(zhǔn)中;例如ASCii字符集桩警,gb2312字符集可训,Unicode字符集等。
編碼: 規(guī)定每個(gè)“字符”分別用一個(gè)字節(jié)還是多個(gè)字節(jié)存儲(chǔ)捶枢,用哪些字節(jié)來存儲(chǔ)握截,這個(gè)規(guī)定就叫做“編碼”。通俗來講烂叔,編碼就是按照規(guī)則對(duì)字符進(jìn)行翻譯成對(duì)應(yīng)的二進(jìn)制數(shù)谨胞,在計(jì)算器中運(yùn)行存儲(chǔ),用戶看的時(shí)候蒜鸡,再解碼顯示成用戶能看得懂的胯努。

2.UTF-8編碼中,字節(jié)和字符的對(duì)應(yīng)關(guān)系逢防。

1個(gè)英文字符 和 英文標(biāo)點(diǎn) = 1個(gè)字節(jié)
1個(gè)中文(含繁體) 和 中文標(biāo)點(diǎn) = 3個(gè)字節(jié)
Emoji表情 和 某些特殊字符 = 4個(gè)字節(jié)

3.String類的length() 和 codePointCount() 的區(qū)別

length():返回的是使用的UTF-16編碼的字符代碼單元數(shù)量叶沛,不一定是我們認(rèn)為的字符個(gè)數(shù)。
codePointCount():返回的是代碼點(diǎn)數(shù)量忘朝,是實(shí)際的字符個(gè)數(shù)灰署。
例如1個(gè)Emoji表情是一個(gè)字符,codePointCount()是1局嘁,但是length()是2溉箕。
原因:
常用的UniCode字符使用一個(gè)代碼單元就可以表示(如英文、數(shù)字悦昵、中文)约巷,但有些輔助字符需要一對(duì)代碼單元表示(如Emoji表情)。length()計(jì)算的是代碼單元的數(shù)量旱捧,codePointCount()計(jì)算的是代碼點(diǎn)數(shù)独郎。

補(bǔ)充:
有的emoji表情codePointCount()是2,length()也是2枚赡,因?yàn)樗鼈兛赡軘y帶著一個(gè)我們看不到的符號(hào)氓癌,代碼打印能看到,這可能就涉及到是否增補(bǔ)字符的問題了贫橙。

實(shí)例

對(duì)含有emoji表情的字符串進(jìn)行UTF-8編解碼


image.png
對(duì)整段字符進(jìn)行編碼

這種方式簡(jiǎn)單便捷贪婉,如無特殊情況,這么處理就足夠了卢肃。最后顯示的都是帶%號(hào)的字符串疲迂,例如:%E5%93%88

try {
     //編碼
    String encodeStr = URLEncoder.encode(src, "UTF-8");
    //解碼
    String decodeStr = URLDecoder.decode(encodeStr, "UTF-8");
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}
對(duì)Emoij表情單獨(dú)編碼

如果遇到特殊要求才顿,只對(duì)emoji表情編碼,其他字符不編碼尤蒿,那就要用到codePointCount()了郑气,解碼的時(shí)候直接對(duì)整段解碼即可。

public class EmojiUtils {

    /**
     * 對(duì)emoji表情單獨(dú)編碼
     * @param src
     * @return
     */
    public static String escape(String src){
        //1.得到代碼點(diǎn)數(shù)量腰池,也即是實(shí)際字符數(shù)尾组,注意和length()的區(qū)別
        //舉例:
        //一個(gè)emoji表情是一個(gè)字符,codePointCount()是1示弓,length()是2讳侨。
        //但是遇到過一個(gè)emoji表情居然帶著空格符號(hào),結(jié)果它的codePointCount()是2奏属,length()也是2跨跨,實(shí)際上單獨(dú)拎emoji來說,它的length()應(yīng)該說是1才對(duì)了囱皿。
        //推測(cè)這就是它們是否屬于增補(bǔ)字符范圍內(nèi)歹叮,length=2的是增補(bǔ)字符,length=1的不是铆帽。
        int cpCount = src.codePointCount(0, src.length());

        //2.得到字符串的第一個(gè)代碼點(diǎn)index咆耿,和最后一個(gè)代碼點(diǎn)index
        //舉例:比如3個(gè)emoji表情,那么它的cpCount=3爹橱;firCodeIndex=0萨螺;lstCodeIndex=4
        //因?yàn)槊總€(gè)emoji表情length()是2,所以第一個(gè)是0-1愧驱,第二個(gè)是2-3慰技,第三個(gè)是4-5
        int firCodeIndex = src.offsetByCodePoints(0, 0);
        int lstCodeIndex = src.offsetByCodePoints(0, cpCount - 1);

        StringBuilder sb = new StringBuilder();
        int index = firCodeIndex;
        while (index<=lstCodeIndex){
            //3.獲得代碼點(diǎn),判斷是否是emoji表情
            //注意组砚,codePointAt(int) 這個(gè)int對(duì)應(yīng)的是codeIndex
            //舉例:3個(gè)emoji表情吻商,取第3個(gè)emoji表情,index應(yīng)該是4
            int codepoint = src.codePointAt(index);
            if (!isEmojiCharacter(codepoint)) {
                sb.append((char) codepoint);
            }else{
                try {
                    //4.對(duì)emoji表情UTF-8編碼糟红,判斷是否是增補(bǔ)字符范圍內(nèi)
                    int length = Character.isSupplementaryCodePoint(codepoint)? 2 : 1;
                    String encoderStr = URLEncoder.encode(src.substring(index,index + length), "UTF-8");
                    sb.append(encoderStr);
                } catch (UnsupportedEncodingException e) {

                }
            }
            //4.確定指定字符(Unicode代碼點(diǎn))是否在增補(bǔ)字符范圍內(nèi)艾帐。
            //因?yàn)槌吮砬椋€有些特殊字符也是在增補(bǔ)字符方位內(nèi)的盆偿。
            index += Character.isSupplementaryCodePoint(codepoint)? 2 : 1;
        }
        return sb.toString();
    }
    
    /**
     * 過濾掉emoji表情
     * @param src
     * @return
     */
    public static String filter(String src) {
        if (src == null) {
            return null;
        }
        //得到codePointCount
        int cpCount = src.codePointCount(0, src.length());
        int firCodeIndex = src.offsetByCodePoints(0, 0);
        int lstCodeIndex = src.offsetByCodePoints(0, cpCount - 1);
        StringBuilder sb = new StringBuilder(src.length());
        for (int index = firCodeIndex; index <= lstCodeIndex;) {
            //遍歷每個(gè)codePoint
            int codepoint = src.codePointAt(index);
            if (!isEmojiCharacter(codepoint)) {
                System.err.println("codepoint:" + Integer.toHexString(codepoint));
                sb.append((char) codepoint);
            }
            index += ((Character.isSupplementaryCodePoint(codepoint)) ? 2 : 1);

        }
        return sb.toString();
    }


    /**
     * 是否是Emoji表情
     * @param codePoint
     * @return
     */
    private static boolean isEmojiCharacter(int codePoint) {
        return (codePoint >= 0x2600 && codePoint <= 0x27BF) // 雜項(xiàng)符號(hào)與符號(hào)字體
                || codePoint == 0x303D || codePoint == 0x2049 || codePoint == 0x203C
                || (codePoint >= 0x2000 && codePoint <= 0x200F)//
                || (codePoint >= 0x2028 && codePoint <= 0x202F)//
                || codePoint == 0x205F //
                || (codePoint >= 0x2065 && codePoint <= 0x206F)//
                /* 標(biāo)點(diǎn)符號(hào)占用區(qū)域 */
                || (codePoint >= 0x2100 && codePoint <= 0x214F)// 字母符號(hào)
                || (codePoint >= 0x2300 && codePoint <= 0x23FF)// 各種技術(shù)符號(hào)
                || (codePoint >= 0x2B00 && codePoint <= 0x2BFF)// 箭頭A
                || (codePoint >= 0x2900 && codePoint <= 0x297F)// 箭頭B
                || (codePoint >= 0x3200 && codePoint <= 0x32FF)// 中文符號(hào)
                || (codePoint >= 0xD800 && codePoint <= 0xDFFF)// 高低位替代符保留區(qū)域
                || (codePoint >= 0xE000 && codePoint <= 0xF8FF)// 私有保留區(qū)域
                || (codePoint >= 0xFE00 && codePoint <= 0xFE0F)// 變異選擇器
                || codePoint >= 0x10000; // Plane在第二平面以上的柒爸,char都不可以存,全部都轉(zhuǎn)
    }
}
參考

https://blog.csdn.net/u014166319/article/details/71308112
https://www.cnblogs.com/mojxtang/p/10154907.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末事扭,一起剝皮案震驚了整個(gè)濱河市捎稚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖今野,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葡公,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡条霜,警方通過查閱死者的電腦和手機(jī)催什,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛔外,“玉大人,你說我怎么就攤上這事溯乒〖醒幔” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵裆悄,是天一觀的道長(zhǎng)矛纹。 經(jīng)常有香客問我,道長(zhǎng)光稼,這世上最難降的妖魔是什么或南? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮艾君,結(jié)果婚禮上采够,老公的妹妹穿的比我還像新娘。我一直安慰自己冰垄,他們只是感情好蹬癌,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著虹茶,像睡著了一般逝薪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蝴罪,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天董济,我揣著相機(jī)與錄音,去河邊找鬼要门。 笑死虏肾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的欢搜。 我是一名探鬼主播询微,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼狂巢!你這毒婦竟也來了撑毛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎藻雌,沒想到半個(gè)月后雌续,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俏蛮,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垦巴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年渺氧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了金句。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跑筝。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坛缕,死狀恐怖聊闯,靈堂內(nèi)的尸體忽然破棺而出史简,到底是詐尸還是另有隱情居暖,我是刑警寧澤顽频,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站太闺,受9級(jí)特大地震影響糯景,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜省骂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一蟀淮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧钞澳,春花似錦怠惶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至逃延,卻和暖如春览妖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背揽祥。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工讽膏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拄丰。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓府树,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親料按。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奄侠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容