从Java字符串中删除✅,🔥,✈,♛和其他此类表情符号/图像/符号


192

我有一些带有各种不同表情符号/图像/符号的字符串。

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

▓ 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.❉

...还有更多

我想摆脱所有这些符号/图像,只保留不同语言中的字母(和标点符号)。

我试图使用EmojiParser库清理标志:

String withoutEmojis = EmojiParser.removeAllEmojis(input);

问题在于EmojiParser无法删除大部分符号。♦符号是我迄今为止发现的唯一符号。signs❉★★such such等其他符号不会被删除。

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


91
你想保留什么?
YCF_L

31
两个问题:什么是EmojiParser?似乎不是标准库的一部分,所以此提述不是很有帮助。您到底要过滤哪些字符?您说的是“更多此类”,但是有许多字符组和家庭。我们需要更多地了解您的标准。
马库斯·菲舍尔

129
IDK您这样做的动机是什么,但是如果它也过滤了文本输入,则不要。我厌倦了被迫使用a-zA-Z。让我用我的母语,表情符号或任何我想要的东西写。我真的要我将日历约会称为“ 🤦🏻‍♂️”吗?是的,是的。现在让开。
亚历山大-恢复莫妮卡

19
请明确说明您要保留和删除的内容。从表面上看,这个问题似乎很清楚,但是由于Unicode的复杂性,所以它不是,因此无法提供一个很好的答案。
奥列格

12
当它破坏了至少一个示例的含义时,这似乎是一件很奇怪的事情?
伊芙

Answers:


290

除了将某些元素列入黑名单之外,如何创建要保留的字符白名单呢?这样,您无需担心每个新表情符号都会被添加。

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}),空格/分隔符(\\p{Z}),其他格式(\\p{Cf})和上述U+FFFFUnicode(\\p{Cs})中的其他字符以及换行符(\\s)。\\p{L}具体包括其他字母的字符,例如西里尔字母,拉丁字母,汉字等。
  • ^在正则表达式字符集否定匹配。

例:

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 文档中的正则表达式。


4
ASCII字母数字字符和表情符号之间的明显差距是加重的和非拉丁字母。没有OP的输入,我们不知道这是否是个好答案(虽然不是我的DV)
Chris H

4
是的,我很好奇为什么这可能会被否决。第二个我看到这个问题,正则表达式是我想到的第一件事(PS,因为他正在寻找标准字符和标点符号,我会使用类似的东西,[^\w\^\-\[\]\.!@#$%&*\(\)/+'":;~?,]但这只是我很健壮,并尝试收集所有't符号)。赞成,因为这绝对是一个潜在的解决方案。如果他想添加一些其他语言字符,则可以根据需要将它们添加到表达式中。
克里斯(Chris

15
@Chris伟大的标点符号正则表达式示例在某些情况下对我来说足够广泛。另外,也许人们没有读完整的答案- 如答案底部所述,p{L}处理非英语字母字符。我希望能理解的是,我不能在回答中列出每个非英语字母,因为那会不切实际。
Nick Bull)

12
这个。谢谢,麻烦您了。不要试图禁止会给您带来麻烦的角色;确定您允许的字符并对其进行编码。然后,您的代码具有一组明确定义的测试用例。
jpmc26

2
我建议"[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\s]"。这允许使用以下常规类别:字母,标记,数字,标点,分隔符和“其他格式”,以及空白字符(如制表符和换行符)。
肖恩·范·哥德

81

我并不精通Java,所以我不会尝试内联编写示例代码,但是我要这样做的方法是检查Unicode称为每个字符的“常规类别”。有几个字母和标点符号类别。

您可以使用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,我没有包括在上述类别白名单中。)


1
格式(CF)也应保留;这包括聚类和方向性覆盖,没有它们,就不可能用某些语言写某些(不常见的,公认的)单词。
zwol

@zwol感谢您提供详细信息!我将其添加到列表中。
Daniel Wagner

29
这是面向未来的答案。无论将来是否对Unicode标准进行更新,基于字符的类别包含/排除字符都意味着不需要单独解析字符和维护列表。当然,应该对不同语言(例如中文,阿拉伯语等)的文本进行粗略的测试,以确保过滤的类别与目标环境中允许的文本匹配。
CJBS

3
哦,昨天我应该想到的另一个陷阱:TAB,CR和LF都是通用类别Cc(Java的CONTROL)。那些需要专门列入白名单,因为你几乎肯定希望让大多数传统控制字符。
zwol

@CJBS这种方法的问题在于它仅在Java中部分实现。例如,Character.getType()不会告诉您您的char(或int代码点,因为该方法已重载)是表情符号,音乐符号还是表情符号字符等。如果您有简单的用例,可能会很好要走这条路-这当然是一种易于理解的优雅方法-但请注意,如果需求发生变化,它可能会失效。
skomisa

47

根据完整表情符号列表v11.0,您需要删除1644个不同的Unicode代码点。例如,在此列表中为U+2705

拥有表情符号的完整列表,您需要使用代码点将其过滤掉。遍历单个字节charbyte因为单个代码点可能跨越多个字节而无法工作。由于Java使用UTF-16表情符号,通常需要2 chars。

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);

28
非常有用的清单。有趣的是,使用名为removeAllEmojis的方法调用EmojiParser不能处理这些问题... :-)
TJ Crowder

7
@Bergi:不,因为input.codePointAt最多只能看到2个字符,这是一个恒定的上限。另外(新添加的)还会i += Character.charCount(cp)跳过所有已input.codePointAt检查的字符(在某些特殊情况下为负1)。
David Foerster,

6
@OlivierGrégoire:String.chars()通过字符而不是代码点流式传输。有一个单独的方法String.codePoints()
David Foerster,

5
这里至少有两个问题:您使用的是表情符号“封闭”列表,因此每年都必须对其进行扩展(但这可能不容易解决),并且此代码可能无法与代码点序列一起正常工作(参见例如unicode.org/Public/emoji/11.0/emoji-zwj-sequences.txt
萨那托斯

49
这基本上与EmojiParser使用的方法相同,并且由于相同的原因很快会失败。新的表情符号相对频繁地添加到Unicode字符数据库中,如果您现在正在使用针对负规则集的当前定义的1644个表情符号来实现解决方案,则一旦有新的表情符号可用,该实现就会失败。
jarnbjo

20

ICU4J是您的朋友。

UCharacter.hasBinaryProperty(UProperty.EMOJI);

请记住要使您的icu4j版本保持最新,并注意这只会过滤掉正式的Unicode表情符号,而不是符号字符。结合根据需要过滤掉其他字符类型。

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


1
在Java更新为包括Emoji二进制属性之前,我想这将是一个很好的解决方案。不过,对于新添加的代码点,该库通常需要更新。
nhahtdh

10

我在下面提供了一些示例,并认为拉丁语就足够了,但是...

有没有一种方法可以从输入字符串中删除所有这些符号,并仅保留不同语言中的字母和标点符号?

编辑后,使用该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
早上好 
早上好 
Καλημέρα 
Καλημέρα 

该代码通过将String流式传输到代码点来工作。然后使用lambda将字符过滤到int数组中,然后将数组转换为String。

字母和空格使用使用字符方法进行筛选,没有标点符号好。尝试失败

Unicode使用程序员指定为允许的unicode块阻止白色过滤器。尝试失败

Unicode使用程序员指定为不允许的unicode块阻止黑色过滤器。尝试失败

类别使用静态方法过滤Character.getType。程序员可以在category数组中定义允许的类型。WORKS 😨😱😰😲😀。


import java.lang.Character.UnicodeBlock;,然后Character.UnicodeBlock-> UnicodeBlock
伯恩哈德·巴克

您的所有方法均未通过测试。
奥列格(Oleg)

@Oleg不,再看一个white list例子。
Marcos Zolnowski

一定出事了与我的眼睛或我的显示器,我也看不出是早上好和Καλημέρα
奥列格

4
请注意,Java语言支持较新的Unicode版本有点慢...例如,Java 10仅支持Unicode 8(因此其字符类仅描述Unicode 8字符)...这么多的表情符号都没有出现(请参阅docs.oracle) .com / javase / 10 / docs / api / java / lang / Character.html字符信息基于Unicode标准,版本8.0.0。
xanatos,


-1

使用一个名为RM-Emoji的jQuery插件。运作方式如下:

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

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


5
问题是在Java中,因此jQuery插件在这里不相关。
riorio '18 -4-14
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.