1. 程式人生 > >過濾字串中的Emoji表情

過濾字串中的Emoji表情

    iOS 5.0之前,蘋果都是採用3個位元組來承接 emoji 表情,Java 的普通 char 可以支援顯示。但 iOS 5.0 之後, 蘋果升級了系統自帶的 emoji 表情輸入法,用的 Unicode 6 標準來統一,是採用4個 bytes 來承接一個 emoji 表情。如果不做處理的話,這種表情直接儲存到 mysql5.5 以下的資料庫是會報錯的。就像這兩個表情一樣:口口, 在 Windows 8 以下估計都不支援顯示,可能會顯示成框框,可能壓根就是空白, 你可以在 Mac 中

使用Safari 瀏覽器中,就可以看到。經過測試,在 Mac 就算用 Chrome 瀏覽器(Version 25.0.1364.172)也是不行的。

這種資料在 Mysql 5.5 之前,UTF-8 支援1-3個位元組的編碼,從 Mysql5.5 開始後,可以支援4個位元組的 UTF 編碼,但要特殊標記。修改 Mysql 相應儲存欄位為 utf8mb4 。修改語句如下: 

1 ALTER TABLE table_name
2   MODIFY COLUMN content varchar(500) CHARACTER
3   SET utf8mb4 COLLATE utf8mb4_unicode_ci
4   DEFAULT
NULL COMMENT 'content of message';
在某種業務情景下,我們可以選擇過濾掉這種“非法”的字元。我採用的方式是,在字元上面做操作,下面是Java示例程式碼,核心的程式碼附上,應該是 無法直接下載就能夠編譯,你得小小的做一些微調,沒有額外的依賴:
01 public class EmojiFilter {
02  
03     /**
04      * 檢測是否有emoji字元
05      * @param source
06      * @return 一旦含有就丟擲
07      */
08     public static boolean containsEmoji(String source) {
09         if (StringUtils.isBlank(source)) {
10             return false;
11         }
12  
13         int len = source.length();
14  
15         for (int i = 0; i < len; i++) {
16             char codePoint = source.charAt(i);
17  
18             if (isEmojiCharacter(codePoint)) {
19                 //do nothing,判斷到了這裡表明,確認有表情字元
20                 return true;
21             }
22         }
23  
24         return false;
25     }
26  
27     private static boolean isEmojiCharacter(char codePoint) {
28         return (codePoint == 0x0) ||
29                 (codePoint == 0x9) ||
30                 (codePoint == 0xA) ||
31                 (codePoint == 0xD) ||
32                 ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
33                 ((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) ||
34                 ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF));
35     }
36  
37     /**
38      * 過濾emoji 或者 其他非文字型別的字元
39      * @param source
40      * @return
41      */
42     public static String filterEmoji(String source) {
43  
44         if (!containsEmoji(source)) {
45             return source;//如果不包含,直接返回
46         }
47         //到這裡鐵定包含
48         StringBuilder buf = null;
49  
50         int len = source.length();
51  
52         for (int i = 0; i < len; i++) {
53             char codePoint = source.charAt(i);
54  
55             if (isEmojiCharacter(codePoint)) {
56                 if (buf == null) {
57                     buf = new StringBuilder(source.length());
58                 }
59  
60                 buf.append(codePoint);
61             } else {
62             }
63         }
64  
65         if (buf == null) {
66             return source;//如果沒有找到 emoji表情,則返回源字串
67         } else {
68             if (buf.length() == len) {//這裡的意義在於儘可能少的toString,因為會重新生成字串
69                 buf = null;
70                 return source;
71             } else {
72                 return buf.toString();
73             }
74         }
75  
76     }
77 }

還有優化的空間,但是已經能夠滿足大多數情況的需求,附上單元測試(JUnit4):

01 public class EmojiFilterTest {
02  
03  
04  /**
05      * 測試emoji表情
06      */
07     @Test
08     public void fileterEmoji() {
09         String s = "<body>口口213這是一個有各種內容的訊息,  Hia Hia Hia !!!! [email protected]@@...*)!" +
10                 "(@*$&@(&#!)@*)!&$!)@^%@(!&#. 口口口], ";
11         String c = Utils.filterEmoji(s);
12         assertFalse(s.equals(c));
13         String expected = "<body>213這是一個有各種內容的訊息,  Hia Hia Hia !!!! [email protected]@@...*)" +
14                 "!(@*$&@(&#!)@*)!&$!)@^%@(!&#. ], ";
15         assertEquals(expected, c);
16 //        assertSame(c, expected);
17         assertSame(expected, "<body>213這是一個有各種內容的訊息,  Hia Hia Hia !!!! [email protected]@@...*)" +
18                 "!(@*$&@(&#!)@*)!&$!)@^%@(!&#. ], ");
19         assertSame(c, Utils.filterEmoji(c));
20     }
21  
22 }

原文連結:http://doombyte.com/blog/2013/03/20/filter-emoji-emotion-in-string/