如何解码表情符号字符的格式不正确的十六进制字符串,如“`1f1e81f1f3`”?
How to decode unwell-formed hex string of emoji character like "`1f1e81f1f3`"?
假设有一个表情符号字符的十六进制字符串,如“1f1e81f1f3
”,它是一个表情符号字符代码点的格式不正确的十六进制字符串,它应该是两个字符串,如1f1e8
1f1f3
我正在使用 org.apache.commons.codec.binary.Hex
解码十六进制字符串,但显然十六进制需要输入字符串的长度是偶数,所以我需要将十六进制字符串设为零填充样式,如“ 01f1e8
01f1f3
".
目前,我只是将“1f”替换为“01f”,到目前为止还不错,但是自从 an emoji glyph may contains a sequence of unicode characters,所以
- 简单地将“1f”替换为“01f”是否安全?
- 如果不安全,如何解码这样的十六进制字符串safely/properly和restore/translate来纠正表情符号character/character_sequence?看来我需要实现自定义 UTF16BE 解码器?
背景
这个十六进制的表情符号字符串是从“<span class="emoji emojiXXXXXXXXXX"></span>
”字符串中剥离出来的,它是通过非官方 HTTP API.
从流行的 IM 软件中检索到的一条文本消息
我最终写了一个小函数来恢复表情符号字符。
基本程序:
- 创建一个指向十六进制字符串开头的指针。
- 从十六进制字符串的指针位置开始查找,
- 如果它以“
1f
”开头,则在“1f
”之前填充三个零,将其存储到一个新的十六进制字符串中,然后指针步进到下一个第 5 个位置。否则,不进行零填充,将子字符串存储到新的十六进制字符串,并将指针步进到下一个第 4 个位置。
- 将新的十六进制字符串解码为字节数组。
- 使用字节数组中的 UTF_32BE 或 UTF_16BE 字符编码创建新字符串。
- 循环到第 2 步,直到十六进制字符串结束。
可以,但不完美,如果
可能会引入错误
- emoji字符序列中的一个字符位于增补字符
- 和
- 它的十六进制字符串不是以“
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;
}
假设有一个表情符号字符的十六进制字符串,如“1f1e81f1f3
”,它是一个表情符号字符代码点的格式不正确的十六进制字符串,它应该是两个字符串,如1f1e8
1f1f3
我正在使用 org.apache.commons.codec.binary.Hex
解码十六进制字符串,但显然十六进制需要输入字符串的长度是偶数,所以我需要将十六进制字符串设为零填充样式,如“ 01f1e8
01f1f3
".
目前,我只是将“1f”替换为“01f”,到目前为止还不错,但是自从 an emoji glyph may contains a sequence of unicode characters,所以
- 简单地将“1f”替换为“01f”是否安全?
- 如果不安全,如何解码这样的十六进制字符串safely/properly和restore/translate来纠正表情符号character/character_sequence?看来我需要实现自定义 UTF16BE 解码器?
背景
这个十六进制的表情符号字符串是从“<span class="emoji emojiXXXXXXXXXX"></span>
”字符串中剥离出来的,它是通过非官方 HTTP API.
我最终写了一个小函数来恢复表情符号字符。
基本程序:
- 创建一个指向十六进制字符串开头的指针。
- 从十六进制字符串的指针位置开始查找,
- 如果它以“
1f
”开头,则在“1f
”之前填充三个零,将其存储到一个新的十六进制字符串中,然后指针步进到下一个第 5 个位置。否则,不进行零填充,将子字符串存储到新的十六进制字符串,并将指针步进到下一个第 4 个位置。 - 将新的十六进制字符串解码为字节数组。
- 使用字节数组中的 UTF_32BE 或 UTF_16BE 字符编码创建新字符串。
- 如果它以“
- 循环到第 2 步,直到十六进制字符串结束。
可以,但不完美,如果
可能会引入错误- emoji字符序列中的一个字符位于增补字符
- 和
- 它的十六进制字符串不是以“
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;
}