开发者社区> 问答> 正文

过滤字符串中的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 。修改语句如下: 

ALTER TABLE table_name
  MODIFY COLUMN content varchar(500) CHARACTER
  SET utf8mb4 COLLATE utf8mb4_unicode_ci
  DEFAULT NULL COMMENT 'content of message';
在某种业务情景下,我们可以选择过滤掉这种“非法”的字符。我采用的方式是,在字符上面做操作,下面是Java示例代码,核心的代码附上,应该是 无法直接下载就能够编译,你得小小的做一些微调,没有额外的依赖:
public class EmojiFilter {

    /**
     * 检测是否有emoji字符
     * @param source
     * @return 一旦含有就抛出
     */
    public static boolean containsEmoji(String source) {
        if (StringUtils.isBlank(source)) {
            return false;
        }

        int len = source.length();

        for (int i = 0; i < len; i++) {
            char codePoint = source.charAt(i);

            if (isEmojiCharacter(codePoint)) {
                //do nothing,判断到了这里表明,确认有表情字符
                return true;
            }
        }

        return false;
    }

    private static boolean isEmojiCharacter(char codePoint) {
        return (codePoint == 0x0) ||
                (codePoint == 0x9) ||
                (codePoint == 0xA) ||
                (codePoint == 0xD) ||
                ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
                ((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) ||
                ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF));
    }

    /**
     * 过滤emoji 或者 其他非文字类型的字符
     * @param source
     * @return
     */
    public static String filterEmoji(String source) {

        if (!containsEmoji(source)) {
            return source;//如果不包含,直接返回
        }
        //到这里铁定包含
        StringBuilder buf = null;

        int len = source.length();

        for (int i = 0; i < len; i++) {
            char codePoint = source.charAt(i);

            if (isEmojiCharacter(codePoint)) {
                if (buf == null) {
                    buf = new StringBuilder(source.length());
                }

                buf.append(codePoint);
            } else {
            }
        }

        if (buf == null) {
            return source;//如果没有找到 emoji表情,则返回源字符串
        } else {
            if (buf.length() == len) {//这里的意义在于尽可能少的toString,因为会重新生成字符串
                buf = null;
                return source;
            } else {
                return buf.toString();
            }
        }

    }
}

还有优化的空间,但是已经能够满足大多数情况的需求,附上单元测试(JUnit4):

public class EmojiFilterTest {


 /**
     * 测试emoji表情
     */
    @Test
    public void fileterEmoji() {
        String s = "<body>口口213这是一个有各种内容的消息,  Hia Hia Hia !!!! xxxx@@@...*)!" +
                "(@*$&@(&#!)@*)!&$!)@^%@(!&#. 口口口], ";
        String c = Utils.filterEmoji(s);
        assertFalse(s.equals(c));
        String expected = "<body>213这是一个有各种内容的消息,  Hia Hia Hia !!!! xxxx@@@...*)" +
                "!(@*$&@(&#!)@*)!&$!)@^%@(!&#. ], ";
        assertEquals(expected, c);
//        assertSame(c, expected);
        assertSame(expected, "<body>213这是一个有各种内容的消息,  Hia Hia Hia !!!! xxxx@@@...*)" +
                "!(@*$&@(&#!)@*)!&$!)@^%@(!&#. ], ");
        assertSame(c, Utils.filterEmoji(c));
    }

}

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

展开
收起
爱吃鱼的程序员 2020-06-22 20:32:22 1104 0
1 条回答
写回答
取消 提交回答
  • https://developer.aliyun.com/profile/5yerqm5bn5yqg?spm=a2c6h.12873639.0.0.6eae304abcjaIB

    能不坑人么?

    isEmojiCharacter这个方法名和方法内容不符合,容易引起误会。filterEmoji方法下面要加source=""+source;return的时候要trim(),否则无法判断单单独表情字符
    不管什么都是true啊!!!我改成isNotEmojiCharacter(charcodePoint)了 publicstaticStringfilterEmoji(Stringsource){if(StringUtils.isBlank(source)){returnsource;}StringBuilderbuf=null;intlen=source.length();for(inti=0;i<len;i++){charcodePoint=source.charAt(i);if(isNotEmojiCharacter(codePoint)){if(buf==null){buf=newStringBuilder(source.length());}buf.append(codePoint);}}if(buf==null){returnsource;}else{if(buf.length()==len){buf=null;returnsource;}else{returnbuf.toString();}}}



    我把方法优化了一下,不然你原有方法其实是要遍历两遍的,现在这个方法就只需要遍历一遍了经测试,这个方法没有用,

    屁用也不管一条

    2020-06-22 20:32:38
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载