如何解码不良形式的表情符号字符串,如"`1f1e81f1f3`"?

时间:2017-07-13 03:39:48

标签: java unicode decode emoji

假设有一个表情符号的十六进制字符串,如" 1f1e81f1f3",它是表情符号的代码点的不正确的十六进制字符串,并且&# 39;应该是1f1e8 1f1f3

这两个字符串

我使用org.apache.commons.codec.binary.Hex来解码十六进制字符串,但显然Hex需要输入字符串的长度是偶数,所以我需要将十六进制字符串设置为零填充样式,如" 0 1f1e8 0 1f1f3"

目前,我只需更换" 1f"与" 01f",到目前为止一直很好,但自an emoji glyph may contains a sequence of unicode characters以来,所以

  • 简单地更换" 1f"是否安全用" 01f" ?
  • 如果它不安全,如何安全/正确地解码这样的十六进制字符串并恢复/翻译它们以纠正表情符号字符/字符序列?我似乎需要实现自定义UTF16BE解码器吗?

背景

这个表情符号的十六进制字符串从&#34; <span class="emoji emojiXXXXXXXXXX"></span>&#34; string,它是通过非官方HTTP API从流行的IM软件中检索的文本消息。

1 个答案:

答案 0 :(得分:0)

我最终写了一个小函数来恢复表情符号字符。

基本程序:

  1. 指向十六进制字符串的开头。
  2. 从十六进制字符串的指针位置搜索,
    • 如果它以“1f”开头,则在“1f”之前填充三个零,将其存储到新的十六进制字符串,然后指针指向下一个第五个位置。否则,不会进行零填充,将子字符串存储到新的十六进制字符串,并指针指向下一个第四个位置。
    • 将新的十六进制字符串解码为字节数组。
    • 使用字节数组中的UTF_32BE或UTF_16BE字符编码创建新字符串。
  3. 循环到第2步,直到十六进制字符串结束。
  4. 它有效,但它并不完美,如果

    ,它可能会引入错误
    • 表情符号字符序列的一个字符位于补充字符
    • 它的十六进制字符串不以“1f”开头,或者十六进制字符串的长度不是5。

    代码段:

    import java.util.*;
    import java.util.regex.*;
    
    import org.apache.commons.codec.*;
    import org.apache.commons.codec.binary.Hex;
    import org.apache.commons.lang3.*;
    
    public static final Charset UTF_32BE = Charset.forName ("UTF-32BE");
    public static final String REGEXP_FindTransformedEmojiHexString = "<span class=\"emoji emoji(\\p{XDigit}+)\"></span>";
    public static final Pattern PATTERN_FindTransformedEmojiHexString = Pattern.compile (REGEXP_FindTransformedEmojiHexString, Pattern.CASE_INSENSITIVE);
    public static String RestoreEmojiCharacters (String sContent)
    {
            bMatched = true;
            String sEmojiHexString = matcher.group(1);
    
            Hex hex = new Hex (StandardCharsets.ISO_8859_1);
            try
            {
                for (int i=0; i<sEmojiHexString.length ();)
                {
                    String sEmoji = null;
                    Charset charset = null;
                    String sSingleEmojiGlyphHexString = null;
                    String sStartString = StringUtils.substring (sEmojiHexString, i, i+2);
                    if (StringUtils.startsWithIgnoreCase (sStartString, "1f"))
                    {
                        sSingleEmojiGlyphHexString = "000" + StringUtils.substring (sEmojiHexString, i, i+5);
                        i += 5;
                        charset = UTF_32BE;
                    }
                    else
                    {
                        sSingleEmojiGlyphHexString = StringUtils.substring (sEmojiHexString, i, i+4);
                        i += 4;
                        charset = StandardCharsets.UTF_16BE;
                    }
                    byte[] arrayEmoji = null;
                    arrayEmoji = (byte[])hex.decode (sSingleEmojiGlyphHexString);
                    sEmoji = new String (arrayEmoji, charset);
                    matcher.appendReplacement (sbReplace, sEmoji);
                }
            }
            catch (DecoderException e)
            {
                e.printStackTrace();
            }
        }
        matcher.appendTail (sbReplace);
    
        if (bMatched)
            sContent = sbReplace.toString ();
    
        return sContent;
    }