从 Java 字符串中删除 ✅, , ✈ , ♛ 和其他类似的 emojis/images/signs

Remove ✅, , ✈ , ♛ and other such emojis/images/signs from Java strings

我有一些字符串,其中包含各种不同的 emojis/images/signs。

并非所有字符串都是英文的——其中一些字符串是其他非拉丁语,例如:

▓ railway??
→ Cats and dogs
I'm on 
Apples ⚛ 
✅ Vi sign
♛ I'm the king ♛ 
Corée ♦ du Nord ☁  (French)
 gjør at både ◄╗ (Norwegian)
Star me ★
Star ⭐ once more
早上好 ♛ (Chinese)
Καλημέρα ✂ (Greek)
another ✓ sign ✓
добрай раніцы ✪ (Belarus)
◄ शुभ प्रभात ◄ (Hindi)
✪ ✰ ❈ ❧ Let's get together ★. We shall meet at 12/10/2018 10:00 AM at Tony's.❉

...还有更多这些。

我想摆脱所有这些 signs/images 并只保留不同语言的字母(和标点符号)。

我尝试使用 EmojiParser library:

清洁标牌
String withoutEmojis = EmojiParser.removeAllEmojis(input);

问题是 EmojiParser 无法删除大部分符号。 ♦ 标志是迄今为止我发现的唯一一个被删除的标志。 其他符号如✪❉★✰❈❧✂❋ⓡ✿♛不去掉

有没有办法从输入字符串中删除所有这些符号,只保留不同语言中的字母和标点符号?

基于Full Emoji List, v11.0 you have 1644 different Unicode code points to remove. For example is on this list as U+2705

拥有完整的表情符号列表后,您需要使用 code points 将它们过滤掉。迭代单个 charbyte 将不起作用,因为单个代码点可以跨越多个字节。因为 Java 使用 UTF-16 表情符号通常需要两个 char

String input = "ab✅cd";
for (int i = 0; i < input.length();) {
  int cp = input.codePointAt(i);
  // filter out if matches
  i += Character.charCount(cp); 
}

从 Unicode 代码点 U+2705 到 Java int 的映射很简单:

int viSign = 0x2705;

或者因为 Java 支持 Unicode 字符串:

int viSign = "✅".codePointAt(0);

与其将某些元素列入黑名单,不如创建一个您希望保留的字符的白名单怎么样?这样您就不必担心每个新的表情符号都会被添加。

String characterFilter = "[^\p{L}\p{M}\p{N}\p{P}\p{Z}\p{Cf}\p{Cs}\s]";
String emotionless = aString.replaceAll(characterFilter,"");

所以:

  • [\p{L}\p{M}\p{N}\p{P}\p{Z}\p{Cf}\p{Cs}\s]是代表所有数字(\p{N})、字母(\p{L})、标记(\p{M})、标点(\p{P})的范围、whitespace/separator (\p{Z})、其他格式 (\p{Cf}) 和 Unicode 中 U+FFFF 以上的其他字符 (\p{Cs}) 和换行符 (\s ) 人物。 \p{L} 特别地 包括来自其他字母表的字符,例如西里尔字母、拉丁字母、汉字等。
  • regex 字符集中的 ^ 否定匹配。

示例:

String str = "hello world _# 皆さん、こんにちは! 私はジョンと申します。";
System.out.print(str.replaceAll("[^\p{L}\p{M}\p{N}\p{P}\p{Z}\p{Cf}\p{Cs}\s]",""));
// Output:
//   "hello world _# 皆さん、こんにちは! 私はジョンと申します。"

如果您需要更多信息,请查看 Java documentation 的正则表达式。

我不是很喜欢 Java,所以我不会尝试编写内联示例代码,但我这样做的方法是检查 Unicode 对每个字符的调用 "the general category" .有几个字母和标点符号类别。

您可以使用 Character.getType 来查找给定字符的一般类别。您可能应该保留属于这些一般类别的那些字符:

COMBINING_SPACING_MARK
CONNECTOR_PUNCTUATION
CURRENCY_SYMBOL
DASH_PUNCTUATION
DECIMAL_DIGIT_NUMBER
ENCLOSING_MARK
END_PUNCTUATION
FINAL_QUOTE_PUNCTUATION
FORMAT
INITIAL_QUOTE_PUNCTUATION
LETTER_NUMBER
LINE_SEPARATOR
LOWERCASE_LETTER
MATH_SYMBOL
MODIFIER_LETTER
MODIFIER_SYMBOL
NON_SPACING_MARK
OTHER_LETTER
OTHER_NUMBER
OTHER_PUNCTUATION
PARAGRAPH_SEPARATOR
SPACE_SEPARATOR
START_PUNCTUATION
TITLECASE_LETTER
UPPERCASE_LETTER

(您列为特别要删除的所有字符都属于一般类别 OTHER_SYMBOL,我没有将其包含在上述类别白名单中。)

我在下面举了一些例子,认为拉丁语就足够了,但是...

Is there a way to remove all these signs from the input string and keeping only the letters & punctuation in the different languages?

编辑后,开发了一个新的解决方案,使用 Character.getType 方法,这似乎是最好的解决方案。

package zmarcos.emoji;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class TestEmoji {

    public static void main(String[] args) {
        String[] arr = {"Remove ✅, , ✈ , ♛ and other such signs from Java string",
            "→ Cats and dogs",
            "I'm on ",
            "Apples ⚛ ",
            "✅ Vi sign",
            "♛ I'm the king ♛ ",
            "Star me ★",
            "Star ⭐ once more",
            "早上好 ♛",
            "Καλημέρα ✂"};
        System.out.println("---only letters and spaces alike---\n");
        for (String input : arr) {
            int[] filtered = input.codePoints().filter((cp) -> Character.isLetter(cp) || Character.isWhitespace(cp)).toArray();
            String result = new String(filtered, 0, filtered.length);
            System.out.println(input);
            System.out.println(result);
        }

        System.out.println("\n---unicode blocks white---\n");
        Set<Character.UnicodeBlock> whiteList = new HashSet<>();
        whiteList.add(Character.UnicodeBlock.BASIC_LATIN);
        for (String input : arr) {
            int[] filtered = input.codePoints().filter((cp) -> whiteList.contains(Character.UnicodeBlock.of(cp))).toArray();
            String result = new String(filtered, 0, filtered.length);
            System.out.println(input);
            System.out.println(result);
        }

        System.out.println("\n---unicode blocks black---\n");
        Set<Character.UnicodeBlock> blackList = new HashSet<>();        
        blackList.add(Character.UnicodeBlock.EMOTICONS);
        blackList.add(Character.UnicodeBlock.MISCELLANEOUS_TECHNICAL);
        blackList.add(Character.UnicodeBlock.MISCELLANEOUS_SYMBOLS);
        blackList.add(Character.UnicodeBlock.MISCELLANEOUS_SYMBOLS_AND_ARROWS);
        blackList.add(Character.UnicodeBlock.MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS);
        blackList.add(Character.UnicodeBlock.ALCHEMICAL_SYMBOLS);
        blackList.add(Character.UnicodeBlock.TRANSPORT_AND_MAP_SYMBOLS);
        blackList.add(Character.UnicodeBlock.GEOMETRIC_SHAPES);
        blackList.add(Character.UnicodeBlock.DINGBATS);
        for (String input : arr) {
            int[] filtered = input.codePoints().filter((cp) -> !blackList.contains(Character.UnicodeBlock.of(cp))).toArray();
            String result = new String(filtered, 0, filtered.length);
            System.out.println(input);
            System.out.println(result);
        }
        System.out.println("\n---category---\n");
        int[] category = {Character.COMBINING_SPACING_MARK, Character.COMBINING_SPACING_MARK, Character.CONNECTOR_PUNCTUATION, /*Character.CONTROL,*/ Character.CURRENCY_SYMBOL,
            Character.DASH_PUNCTUATION, Character.DECIMAL_DIGIT_NUMBER, Character.ENCLOSING_MARK, Character.END_PUNCTUATION, Character.FINAL_QUOTE_PUNCTUATION,
            /*Character.FORMAT,*/ Character.INITIAL_QUOTE_PUNCTUATION, Character.LETTER_NUMBER, Character.LINE_SEPARATOR, Character.LOWERCASE_LETTER,
            /*Character.MATH_SYMBOL,*/ Character.MODIFIER_LETTER, /*Character.MODIFIER_SYMBOL,*/ Character.NON_SPACING_MARK, Character.OTHER_LETTER, Character.OTHER_NUMBER,
            Character.OTHER_PUNCTUATION, /*Character.OTHER_SYMBOL,*/ Character.PARAGRAPH_SEPARATOR, /*Character.PRIVATE_USE,*/
            Character.SPACE_SEPARATOR, Character.START_PUNCTUATION, /*Character.SURROGATE,*/ Character.TITLECASE_LETTER, /*Character.UNASSIGNED,*/ Character.UPPERCASE_LETTER};
        Arrays.sort(category);
        for (String input : arr) {
            int[] filtered = input.codePoints().filter((cp) -> Arrays.binarySearch(category, Character.getType(cp)) >= 0).toArray();
            String result = new String(filtered, 0, filtered.length);
            System.out.println(input);
            System.out.println(result);
        }
    }

}

输出:

---only letters and spaces alike---

Remove ✅, , ✈ , ♛ and other such signs from Java string
Remove      and other such signs from Java string
→ Cats and dogs
 Cats and dogs
I'm on 
Im on 
Apples ⚛ 
Apples  
✅ Vi sign
 Vi sign
♛ I'm the king ♛ 
 Im the king  
Star me ★
Star me 
Star ⭐ once more
Star  once more
早上好 ♛
早上好 
Καλημέρα ✂
Καλημέρα 

---unicode blocks white---

Remove ✅, , ✈ , ♛ and other such signs from Java string
Remove , ,  ,  and other such signs from Java string
→ Cats and dogs
 Cats and dogs
I'm on 
I'm on 
Apples ⚛ 
Apples  
✅ Vi sign
 Vi sign
♛ I'm the king ♛ 
 I'm the king  
Star me ★
Star me 
Star ⭐ once more
Star  once more
早上好 ♛

Καλημέρα ✂


---unicode blocks black---

Remove ✅, , ✈ , ♛ and other such signs from Java string
Remove , ,  ,  and other such signs from Java string
→ Cats and dogs
→ Cats and dogs
I'm on 
I'm on 
Apples ⚛ 
Apples  
✅ Vi sign
 Vi sign
♛ I'm the king ♛ 
 I'm the king  
Star me ★
Star me 
Star ⭐ once more
Star  once more
早上好 ♛
早上好 
Καλημέρα ✂
Καλημέρα 

---category---

Remove ✅, , ✈ , ♛ and other such signs from Java string
Remove , ,  ,  and other such signs from Java string
→ Cats and dogs
 Cats and dogs
I'm on 
I'm on 
Apples ⚛ 
Apples  
✅ Vi sign
 Vi sign
♛ I'm the king ♛ 
 I'm the king  
Star me ★
Star me 
Star ⭐ once more
Star  once more
早上好 ♛
早上好 
Καλημέρα ✂
Καλημέρα 

代码通过将字符串流式传输到 code-points 来工作。然后使用 lambdas 将字符过滤成 int 数组,然后我们将数组转换为 String.

字母和空格是用Character的方法过滤的,不好用标点符号。 尝试失败

unicode blocks white 过滤器使用程序员指定为允许的 unicode 块。 尝试失败

unicode blocks black 过滤器使用程序员指定为不允许的 unicode 块。 尝试失败

类别过滤器使用静态方法Character.getType。程序员可以在 category 数组中定义允许的类型。 作品.

ICU4J 是你的朋友。

UCharacter.hasBinaryProperty(UProperty.EMOJI);

记得保持你的 icu4j 版本是最新的,注意这只会过滤掉官方的 Unicode 表情符号,而不是符号字符。根据需要结合过滤掉其他字符类型。

更多信息: http://icu-project.org/apiref/icu4j/com/ibm/icu/lang/UProperty.html#EMOJI

使用名为 RM-Emoji 的 jQuery 插件。它是这样工作的:

$('#text').remove('emoji').fast()

这是快速模式,可能会遗漏一些表情符号,因为它使用启发式算法在文本中查找表情符号。使用 .full() 方法扫描整个字符串并删除所有保证的表情符号。

试试这个项目simple-emoji-4j

兼容 Emoji 12.0 (2018.10.15)

简单:

EmojiUtils.removeEmoji(str)