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' ; |
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 !!!! xxxx@@@...*)!" + |
10 |
"(@*$&@(&#!)@*)!&$!)@^%@(!&#. 口口口], " ; |
11 |
String c = Utils.filterEmoji(s); |
12 |
assertFalse(s.equals(c)); |
13 |
String expected = "<body>213这是一个有各种内容的消息, Hia Hia Hia !!!! xxxx@@@...*)" + |
14 |
"!(@*$&@(&#!)@*)!&$!)@^%@(!&#. ], " ; |
15 |
assertEquals(expected, c); |
16 |
// assertSame(c, expected); |
17 |
assertSame(expected, "<body>213这是一个有各种内容的消息, Hia Hia Hia !!!! xxxx@@@...*)" + |
18 |
"!(@*$&@(&#!)@*)!&$!)@^%@(!&#. ], " ); |
19 |
assertSame(c, Utils.filterEmoji(c)); |
20 |
} |
21 |
22 |
} |
原文链接:http://doombyte.com/blog/2013/03/20/filter-emoji-emotion-in-string/