小序


对于 EditText 下输入表情的处理已是老生长谈,此前仅需禁止输入即可。
固不懂得这表情在前后端上,究竟需采取何种方式上传获取???带着这种疑惑 …

QA 调查

1. 为何表情符号无法正常上传

数据库无正确指定存储字段的编码方式(utf8mb4,该编码方式需数据库为 5.5.3 即以上版本);

2. 数据库版本不支持或修改影响大怎么办

还能怎么办,只能由前端自行解决问题了。而解决问题关键就是将特定的表情符号转为与数据故对应的编码方式。具体方案有以下两种:

  • 拟定【表情Img-表情符号】对照表
  • 上传内容加密/转义处理

3. 表情Img-表情符号

该处理方案一般作用于聊天窗口下的自定义输入法,自带表情包那种。当然个人并没具体实践过,所以不多做辩解。其具体形式如下(仅参考)

public static final int[] EmojiResArray = {
    R.drawable.d_aini,R.drawable.d_aoteman,R.drawable.d_baibai,
    R.drawable.d_chanzui,R.drawable.d_chijing,R.drawable.d_dahaqi,
    R.drawable.d_feizao,R.drawable.d_ganmao,R.drawable.d_guzhang,
    ...
};

public static final String[] EmojiTextArray = {
    "[爱你]","[奥特曼]","[拜拜]","[悲伤]","[鄙视]","[闭嘴]","[馋嘴]",
    "[吃惊]","[哈欠]","[打脸]","[顶]","[doge]","[肥皂]","[感冒]",
    "[鼓掌]",
    ...
};

4. 加密/转义

操作

描述

URLEncoder.encode(content,”UTF-8”)

可自动识别表情符号,对其进行编码/解码。但若数据库仍未实现 utf8mb4 编码依旧插入数据失败

Base64.decode(content, Base64.DEFAULT)

可实现但加密导致记录值过长,且无参照物作用于仅表情符号的加解密处理,容易导致其余正常文本信息出现解密时显示错乱

Unicode

针对字符转义为对应 unicode 编码,通用性强。对比 Base64 加密处理,虽记录值也随之变长,但可通过参照物 “\u” 以及表情特定符号,仅对其进行转义处理

实现

基于以上折腾,最后前端与APP端统一采取 Unicode 表情符转义处理。直接上代码:

1. Android

/**
 * 把 Unicode 中的表情符号转成表情图片
 *
 * @param unicodeStr
 * @return
 */
public static String unicode2Str(String unicodeStr) {
    String[] asciis = unicodeStr.split("\\\\u");
    String nativeValue = asciis[0];
    try {
        for (int i = 1; i < asciis.length; i++) {
            String code = asciis[i];
            nativeValue += (char) Integer.parseInt(code.substring(0, 4), 16);
            if (code.length() > 4) {
                nativeValue += code.substring(4, code.length());
            }
        }
    } catch (NumberFormatException e) {
        return unicodeStr;
    }
    return nativeValue;
}

/**
 * 把中文中的表情转成 Unicode 码
 *
 * @param str
 * @return
 */
public static String str2Unicode(String str) {
    String result = "";

    if (!TextUtils.isEmpty(str)) {
        for (int i = 0; i < str.length(); i++) {
            char chr1 = str.charAt(i);
            if (isEmojiCharacter(chr1)) {
                result += "\\u".concat(Integer.toHexString(chr1));
            } else {
                result += str.charAt(i);
            }
        }
    }
    return result;
}

/**
 * 是否包含表情
 *
 * @param codePoint
 * @return 如果不包含 返回false,包含 则返回true
 */
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)));
}

2. IOS

+ (NSString *)emojiEncoding:(NSString *)string
{
    NSString *tempStr1 = [string stringByReplacingOccurrencesOfString:@"\\u"withString:@"\\U"];
    NSString *tempStr2 = [tempStr1 stringByReplacingOccurrencesOfString:@"\""withString:@"\\\""];
    NSString *tempStr3 = [[@"\""stringByAppendingString:tempStr2] stringByAppendingString:@"\""];
    NSData *tempData = [tempStr3 dataUsingEncoding:NSUTF8StringEncoding];
    NSString* returnStr = [NSPropertyListSerialization propertyListFromData:tempData
                                                           mutabilityOption:NSPropertyListImmutable
                                                                     format:NULL
                                                           errorDescription:NULL];

    return [returnStr stringByReplacingOccurrencesOfString:@"\\r\\n"withString:@"\n"];
}

+ (NSString *)emojiDecoding:(NSString *)string
{
    if (IsStrEmpty(string))
    {
        return string;
    }
    //正则表达式
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\\ud83c\\udd23-\\ud83e\\udfff]|[\\ud83d\\udd23-\\ud83e\\udfff]|[\\u2600-\\u27ff]" options:0 error:nil];
    //筛选出匹配的字段
    NSMutableArray *matches = [[NSMutableArray alloc] initWithArray:[regex matchesInString:string options:0 range:NSMakeRange(0, string.length)]];

    NSMutableString *resultStr = string.mutableCopy;
    for (int i = (int)matches.count; i > 0; i--)
    {
        NSTextCheckingResult *result = matches[i-1];
        NSRange matchRange = [result range];
        NSString *emoji = [resultStr substringWithRange:matchRange];
        NSString *uniStr = [NSString stringWithUTF8String:[emoji UTF8String]];
        NSData *uniData = [uniStr dataUsingEncoding:NSNonLossyASCIIStringEncoding];
        [resultStr replaceCharactersInRange:matchRange withString:[[NSString alloc] initWithData:uniData encoding:NSUTF8StringEncoding]];
    }

    return resultStr;
}

3.Web

$("#id").text(unescape($(obj).attr("rel").replace(/\\u/g, '%u')));

结语

Android 我也是参考前辈们改改的,当然 IOS,WEB 只是照贴的(偷笑)。需要注意的是,对于APP端使用到的正则函数,某些表情或因官方新的 Emoji 表情包更新,而没有办法识别需自行修改正则表达式。如果有更好解决方案的话,请在评论技能冷却完后于评论区放招。