使用Java實(shí)現(xiàn)查找并移除字符串中的Emoji
一、基礎(chǔ)知識
- Emoji 實(shí)際上是 UTF-8 (Unicode) 字符集上的特殊字符,多數(shù)基本 Emoji 都被分配到 Unicode 編碼表 1 號平面的 U+1F300–1F6FF 和 U+1F900–1FAFF 兩個(gè)區(qū)域,由2個(gè)字符組成。
- 膚色修飾:大多數(shù)與人相關(guān)的 Emoji 默認(rèn)是黃色的,所以后來引入了五個(gè)新碼點(diǎn)作為修飾符:
U+1F3FB、U+1F3FC、U+1F3FD、U+1F3FE、U+1F3FF。膚色修飾符追加到現(xiàn)有的 Emoji 后形成新的樣式:U+1F44B(?? ) +U+1F3FD= ???? - 符號變體或組合:一個(gè)普通的字后連接一個(gè)或多個(gè)變體、組合標(biāo)識(字符),組合形成的 Emoji :
U+25C0+U+FE0F= ??U+27A1+U+FE0F= ??1+U+FE0F+U+20E3= 1?? - 國旗:每個(gè)國旗由2個(gè)地區(qū)標(biāo)識符組合而成,地區(qū)標(biāo)識符的對應(yīng)碼點(diǎn)范圍為
U+1F1E6~U+1F1FF,等同于2個(gè)指定范圍的普通 Emoji 字符組成。U+1F1E8+U+1F1F3= ???? - 零寬度連接符(ZWJ):多個(gè)基礎(chǔ) Emoji 通過零寬度連接符(
U+200D)形成的復(fù)雜 Emoji: ??+U+200D+??= ???? ??+U+200D+??+U+200D+??= ?????? ??+U+200D+??+U+200D+??+U+200D+??= ???????? - 序列:一個(gè)基礎(chǔ) Emoji 加上多個(gè)標(biāo)簽字符 (
U+E0020~U+E007F)并以 Tag Cancel(U+E007)結(jié)尾,組合形成一個(gè)復(fù)雜 Emoji:U+1F3F4(??) +U+E0067+U+E0062+U+E0065+U+E006E+U+E0067+U+E007F= ?????????????? - 特殊符號: 特殊符號只有1個(gè)字符,有些符號在某些環(huán)境下會被當(dāng)做Emoj處理:?、?、?;
Unicode 只是約定了碼點(diǎn)到 emoji 的映射關(guān)系,并沒有約定 Emoji 圖形,每個(gè) Emoji 字體文件可以按照自己的想法設(shè)計(jì) Emoji。
二、解決方案
除了一些特殊符號形式的 Emoji,其他Emoji至少有2個(gè)字符,所以先根據(jù)第二個(gè)字符類型判斷是否為Emoji,使用Character.UnicodeBlock.of和Character.getType方法判定每個(gè)字符的類型。
通過第二個(gè)字符類型判斷當(dāng)前2個(gè)字符為 Emoji 后: 1)判斷是否有后續(xù)修飾 2)判斷處理國旗類型;判斷處理膚色修飾;判斷處理 Emoji 序列標(biāo)簽;判斷處理零寬度連接符;判斷處理連續(xù)變體、組合標(biāo)識;按照普通 Emoji 處理;
處理單字符的特殊符號,這一類型內(nèi)有的屬于 Emoji,有的不是,目前全部簡單的按照普通 Emoji 處理;
三、完整代碼
package com.zpf.tool;
import java.util.List;
public class EmojiUtil {
public static boolean isEmojiNationalFlag(int codePoint) {
return codePoint >= 127462 && codePoint <= 127487;
}
// String str = new String(new int[]{0x1F44B, 0x1F3FD}, 0, 2);
public static boolean isEmojiSkinColor(int codePoint) {
return codePoint >= 127995 && codePoint <= 127999;
}
// String str = new String(new int[]{0x1F3F4, 0xE0067, 0xE0062, 0xE0065, 0xE006E, 0xE0067, 0xE007F}, 0, 7);
public static boolean isEmojiTagEnd(int codePoint) {
return codePoint == 917631;
}
public static boolean isEmojiTagSpec(int codePoint) {
return codePoint >= 917536 && codePoint <= 917630;
}
public static boolean isEmojiDecorateBlock(Character.UnicodeBlock block) {
if (block == null) {
return false;
}
return block.equals(Character.UnicodeBlock.VARIATION_SELECTORS)
|| block.equals(Character.UnicodeBlock.VARIATION_SELECTORS_SUPPLEMENT)
|| block.equals(Character.UnicodeBlock.COMBINING_HALF_MARKS)
|| block.equals(Character.UnicodeBlock.COMBINING_MARKS_FOR_SYMBOLS)
|| block.equals(Character.UnicodeBlock.COMBINING_DIACRITICAL_MARKS)
|| block.equals(Character.UnicodeBlock.COMBINING_DIACRITICAL_MARKS_SUPPLEMENT);
}
public static void pickAllEmoji(CharSequence data, StringBuilder removeResult, List<String> emojiList) {
if (removeResult == null && emojiList == null) {
return;
}
if (removeResult != null) {
removeResult.delete(0, removeResult.length());
}
if (emojiList != null) {
emojiList.clear();
}
if (data == null || data.length() == 0) {
return;
}
StringBuilder emojiBuilder = new StringBuilder();
int i = 0;
int j;
Character.UnicodeBlock block;
while (i < data.length()) {
if (i + 1 < data.length()) {
block = Character.UnicodeBlock.of(data.charAt(i + 1));
if (isEmojiDecorateBlock(block) || Character.UnicodeBlock.LOW_SURROGATES.equals(block)) {
if (i + 2 >= data.length()) {
emojiBuilder.append(data, i, i + 2);
break;
}
j = handleNationalFlag(data, i, emojiBuilder, emojiList);
if (i != j) {
i = j;
continue;
}
j = handleHumanSkin(data, i, emojiBuilder, emojiList);
if (i != j) {
i = j;
continue;
}
j = handleTagSequence(data, i, emojiBuilder, emojiList);
if (i != j) {
i = j;
continue;
}
emojiBuilder.append(data, i, i + 2);
i = handleNextChar(data, i + 2, emojiBuilder, emojiList);
continue;
}
}
recordEmoji(emojiBuilder, emojiList);
int type = Character.getType(data.charAt(i));
if (type == (int) Character.OTHER_SYMBOL) {//特殊符號一律按照Emoji處理
if (emojiList != null) {
emojiList.add(String.valueOf(data.charAt(i)));
}
} else if (removeResult != null) {
removeResult.append(data.charAt(i));
}
i++;
}
recordEmoji(emojiBuilder, emojiList);
}
private static int handleNextChar(CharSequence data, int i, StringBuilder emojiBuilder, List<String> emojiList) {
if (i >= data.length()) {
return i;
}
char nextChar = data.charAt(i);
if (nextChar == '\u200D') {//零寬度連接符
emojiBuilder.append(nextChar);
return i + 1;
}
int j = i;
Character.UnicodeBlock block;
while (j < data.length()) {
nextChar = data.charAt(j);
block = Character.UnicodeBlock.of(nextChar);
if (isEmojiDecorateBlock(block)) {
emojiBuilder.append(nextChar);
j++;
} else {
break;
}
}
if (i != j) {
recordEmoji(emojiBuilder, emojiList);
}
return j;
}
private static int handleNationalFlag(CharSequence data, int i, StringBuilder emojiBuilder, List<String> emojiList) {
int codePoint = Character.codePointAt(data, i);
if (isEmojiNationalFlag(codePoint)) {//處理國旗類型
recordEmoji(emojiBuilder, emojiList);//提交未處理
if (i + 3 < data.length()) {
codePoint = Character.codePointAt(data, i + 2);
if (isEmojiNationalFlag(codePoint)) {
emojiBuilder.append(data, i, i + 4);
recordEmoji(emojiBuilder, emojiList);
i = i + 4;
}
}
i = i + 2;
}
return i;
}
private static int handleHumanSkin(CharSequence data, int i, StringBuilder emojiBuilder, List<String> emojiList) {
if (i + 3 >= data.length()) {
return i;
}
int codePoint = Character.codePointAt(data, i + 2);
if (isEmojiSkinColor(codePoint)) {//膚色修飾
emojiBuilder.append(data, i, i + 4);
recordEmoji(emojiBuilder, emojiList);
i = i + 4;
}
return i;
}
private static int handleTagSequence(CharSequence data, int i, StringBuilder emojiBuilder, List<String> emojiList) {
if (i + 3 >= data.length()) {
return i;
}
int codePoint = Character.codePointAt(data, i + 2);
if (isEmojiTagSpec(codePoint)) {
emojiBuilder.append(data, i, i + 4);
i = i + 4;
while (i < data.length()) {
codePoint = Character.codePointAt(data, i);
if (isEmojiTagSpec(codePoint)) {
emojiBuilder.append(data, i, i + 2);
i = i + 2;
} else if (isEmojiTagEnd(codePoint)) {
emojiBuilder.append(data, i, i + 2);
recordEmoji(emojiBuilder, emojiList);
i = i + 2;
break;
} else { //error
break;
}
}
emojiBuilder.delete(0, emojiBuilder.length());
} else if (isEmojiTagEnd(codePoint)) {
emojiBuilder.append(data, i, i + 4);
recordEmoji(emojiBuilder, emojiList);
i = i + 4;
}
return i;
}
private static void recordEmoji(StringBuilder builder, List<String> emojiList) {
if (builder != null && builder.length() > 0) {
if (emojiList != null) {
emojiList.add(builder.toString());
}
builder.delete(0, builder.length());
}
}
}
以上就是使用Java實(shí)現(xiàn)查找并移除字符串中的Emoji的詳細(xì)內(nèi)容,更多關(guān)于Java查找并移除字符串中Emoji的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決springboot文件上傳提示臨時(shí)文件夾不存在問題
這篇文章主要介紹了解決springboot文件上傳提示臨時(shí)文件夾不存在問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
SpringBoot通過源碼探究靜態(tài)資源的映射規(guī)則實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot通過源碼探究靜態(tài)資源的映射規(guī)則實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
SpringBoot實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離的3種方法小結(jié)
為了提高系統(tǒng)的讀寫性能和可用性,讀寫分離是一種經(jīng)典的數(shù)據(jù)庫架構(gòu)模式,在SpringBoot應(yīng)用中,有多種方式可以實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離,本文將介紹三種主實(shí)現(xiàn)方案,大家可以根據(jù)需要自行選擇2025-04-04
Java實(shí)現(xiàn)本地文件批量重命名的示例代碼
本文主要介紹了Java實(shí)現(xiàn)本地文件批量重命名的示例代碼,主要步驟為獲取指定目錄下的所有文件,對每個(gè)文件進(jìn)行修改,將修改后的文件名賦給該文件,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
SpringBoot封裝MinIO工具的實(shí)現(xiàn)步驟
MinIO是一款高性能、開源、兼容Amazon?S3?API的分布式對象存儲系統(tǒng),Spring?Boot通過封裝配置與工具類,標(biāo)準(zhǔn)化設(shè)計(jì)降低開發(fā)復(fù)雜度,提升開發(fā)效率與系統(tǒng)穩(wěn)定性,感興趣的可以了解一下2025-07-07
SpringBoot集成Redis實(shí)現(xiàn)訂單超時(shí)管理的詳細(xì)步驟
文章描述了如何使用Redis來處理超時(shí)支付訂單,包括引入依賴、配置連接、定義實(shí)體類、實(shí)現(xiàn)服務(wù)和監(jiān)聽器等步驟,文章討論了優(yōu)化方案、注意事項(xiàng)以及Redis的性能和可靠性問題,感興趣的朋友跟隨小編一起看看吧2026-03-03

