如何使用ICU4C检测字符串中的Emoji字符?
如何用ICU4C准确识别UTF-8字符串中的RGI Emoji字符簇
我现在用ICU4C处理UTF-8字符串,目标是找出其中属于Emoji的字符簇。目前的代码已经接近目标,但会误判普通的#为Emoji——因为#️⃣这个Emoji以#开头,所以单独的#也带有UCHAR_EMOJI属性。
我觉得最佳方案是检查RGI_Emoji属性,但这是字符串属性而非码点属性,不知道怎么实现。如果可行的话,我想把每个字符簇作为字符串来检测这个属性,不过文档说没法用正则表达式获取这类字符串属性。
const std::string s8 = "#🤙🏿asd🧔🏼😵💫dds🫥😶🌫️🏌️♂️🇨🇦ds#️⃣🏋🏽ds👨👩👦👦ds👩🏾❤️💋👨🏼ds"; const icu::UnicodeString us = icu::UnicodeString::fromUTF8(s8); UErrorCode status = U_ZERO_ERROR; icu::BreakIterator* bi = icu::BreakIterator::createCharacterInstance(icu::Locale::getUS(), status); bi->setText(us); bool is_emoji = false; for(int32_t e = bi->first(), b = e; e != icu::BreakIterator::DONE; b = e, e = bi->next()) { // Analyze character for emoji-ness. for(int32_t i = b; i != e; ++i) { std::cout << us.char32At(i) << ' '; is_emoji = u_hasBinaryProperty(us.char32At(i), UProperty::UCHAR_EMOJI) || u_hasBinaryProperty(us.char32At(i), UProperty::UCHAR_EMOJI_COMPONENT); } if(is_emoji) { std::cout << "<- is emoji\n"; ++emojis; is_emoji = false; } else { std::cout << "<- is not emoji\n"; } ++characters; } delete bi;
解决方案:使用ICU4C的uc_hasStringProperty检测RGI Emoji
要准确识别RGI Emoji,你需要使用ICU的字符串属性检测API uc_hasStringProperty,它可以针对整个字符簇(而非单个码点)判断是否属于RGI Emoji。
修改后的核心逻辑如下:
- 提取
BreakIterator拆分出的每个字符簇(b到e区间)对应的UnicodeString子串。 - 调用
uc_hasStringProperty,传入子串的码点数组、长度,以及UCHAR_RGI_EMOJI属性。
修改后的代码片段:
// 替换原循环内的emoji判断逻辑 icu::UnicodeString cluster = us.tempSubStringBetween(b, e); const UChar32* clusterCodepoints = cluster.getBuffer32(); int32_t clusterLength = cluster.length32(); is_emoji = uc_hasStringProperty(clusterCodepoints, clusterLength, UProperty::UCHAR_RGI_EMOJI, &status); if (U_FAILURE(status)) { // 处理错误,重置状态避免影响后续操作 status = U_ZERO_ERROR; is_emoji = false; }
关键说明:
UCHAR_RGI_EMOJI是专门用于检测符合Unicode标准的RGI(Recommended for General Interchange)Emoji的字符串属性,只会返回完整有效Emoji簇的结果,不会误判单个#这类组件。- 必须基于
BreakIterator拆分的完整字符簇进行检测,因为Emoji可能由多个码点组成(比如肤色修饰符、组合序列等)。 - 每次调用
uc_hasStringProperty后要检查错误码,防止异常状态影响后续逻辑。
内容的提问来源于stack exchange,提问作者screwnut
相关产品推荐
相关产品推荐

