ChatGPT解决这个技术问题 Extra ChatGPT

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

我有一些字符串,其中包含各种不同的表情符号/图像/标志。

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

▓ 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 library 清理标志:

String withoutEmojis = EmojiParser.removeAllEmojis(input);

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

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

你想保留什么?
两个问题:什么是EmojiParser?似乎不是标准库的一部分,所以这个提及不是很有帮助。您到底要过滤哪些字符?你说“这种类型的还有很多”,但是有很多角色组和家庭。我们需要更多地了解您的标准。
IDK 你背后的动机是什么,但如果它过于过滤文本输入:不要。我厌倦了被迫使用 a-zA-Z。让我用我的母语、表情符号或任何我想要的东西来写。我真的希望我的日历约会被称为“🤦🏻‍♂️”吗?是的,是的,我愿意。现在让开我的路。
请说明您要保留和删除的确切内容。从表面上看,这个问题似乎很清楚,但由于 Unicode 的复杂性,它并非如此,因此不可能提供一个好的答案。
当它破坏至少一个示例的含义时,这似乎是一件奇怪的事情?

N
Nick Bull

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

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}) 和 Unicode 中 U+FFFF 以上的其他字符 (\\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 documentation 中的正则表达式。


ASCII 字母数字字符和表情符号之间的明显差距是重音字母和非拉丁字母。如果没有 OP 对此的输入,我们不知道这是否是一个好的答案(虽然不是我的 DV)
是的,我很好奇为什么这可能会被否决。看到这个问题的第二个,正则表达式绝对是我想到的第一件事(PS,因为他正在寻找标准字符和标点符号,我会使用 [^\w\^\-\[\]\.!@#$%&*\(\)/+'":;~?,] 之类的东西,但这只是我很健壮并尝试收集所有典型的不是符号的字符)。赞成,因为这绝对是一个潜在的解决方案。如果他想添加一些其他语言字符,他可以根据需要将它们添加到表达式中。
@Chris 很棒的标点符号正则表达式示例,在某些情况下对我来说看起来足够广泛。也可能人们没有阅读整个答案 - 如答案底部所述,p{L} 处理非英文字母字符。我希望大家理解,我不能在我的回答中广泛列出所有非英文字母,因为那将是不切实际的冗长。
这个。谢谢,麻烦您了。不要试图禁止给你带来麻烦的字符;决定您允许的字符并对其进行编码。然后你的代码就有了一组明确定义的测试用例。
我建议"[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\s]"。这允许通用类别字母、标记、数字、标点符号、分隔符和“其他、格式”,以及空格字符,例如制表符和换行符。
D
Daniel Wagner

我对 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,我没有将其包括在上述类别白名单中。)


FORMAT (Cf) 也应保留;这包括聚类和方向覆盖,没有它们就不可能在某些语言中写出某些(不寻常的,不可否认的)单词。
@zwol 感谢您的详细信息!我会把它添加到列表中。
这是面向未来的答案。不管将来对 Unicode 标准的更新如何,根据其类别包含/排除字符意味着无需单独解析字符和维护列表。当然,应该对不同语言(例如中文、阿拉伯语等)的文本进行粗略测试,以确保过滤后的类别与目标环境中需要允许的文本相匹配。
哦,我昨天应该想到的另一个问题:TAB、CR 和 LF 都是通用类别 Cc(Java 的 CONTROL)。这些需要特别列入白名单,因为您几乎肯定不想允许大多数旧版控制字符。
@CJBS这种方法的问题在于它只在Java中部分实现。例如,Character.getType() 不会告诉您您的 char(或 int 代码点,因为该方法已重载)是否是表情符号、音乐符号或表情符号等。如果您有一个简单的用例,沿着这条路走下去可能很好——这当然是一种易于理解的优雅方法——但请注意,如果需求发生变化,它可能会中断。
K
Karol Dowbecki

根据 Full Emoji List, v11.0,您有 1644 个不同的 Unicode 代码点要删除。例如 在此列表中为 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);

非常有用的清单。有趣的是,带有名为 removeAllEmojis 的方法的 EmojiParser 无法处理这些... :-)
@Bergi:不,因为 input.codePointAt 最多只能查看 2 个字符,这是一个恒定的上限。此外(新添加的)i += Character.charCount(cp) 会跳过 input.codePointAt 检查的所有字符(在某些极端情况下减去 1)。
@OlivierGrégoire:String.chars() 流过字符而不是代码点。为此有一个单独的方法 String.codePoints()
这里至少有两个问题:您使用的是“封闭”的表情符号列表,因此每年您都必须扩展它(但这可能不容易解决),并且此代码可能无法与代码点序列一起正常工作(参见示例 unicode.org/Public/emoji/11.0/emoji-zwj-sequences.txt
这与 EmojiParser 使用的方法基本相同,并且由于相同的原因很快就会失败。新的表情符号相对频繁地添加到 Unicode 字符数据库中,如果您现在使用当前定义的 1644 个表情符号作为否定规则集来实施解决方案,那么一旦新的表情符号可用,实施就会失败。
D
Daniel F

ICU4J 是你的朋友。

UCharacter.hasBinaryProperty(UProperty.EMOJI);

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

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


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

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

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

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

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

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

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

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

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


import java.lang.Character.UnicodeBlock;,然后是 Character.UnicodeBlock -> UnicodeBlock
你所有的方法都没有通过测试。
@Oleg 不,再看一下 white list 示例。
我的眼睛或显示器一定有问题,我看不到早上好和 Καλημέρα
请注意,Java 语言支持较新的 Unicode 版本有点慢...例如,Java 10 仅支持 Unicode 8(因此其字符类仅描述 Unicode 8 字符)...没有显示很多表情符号(请参阅 docs.oracle.com/javase/10/docs/api/java/lang/Character.html , 字符信息基于 Unicode 标准 8.0.0 版。)
l
liheyuan

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

兼容表情符号 12.0 (2018.10.15)

简单:

EmojiUtils.removeEmoji(str)

它对我不起作用:“🤑 💘”,结果是“🤑”
A
Adil B

使用一个名为 RM-Emoji 的 jQuery 插件。以下是它的工作原理:

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

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


问题是在 Java 中,因此 jQuery 插件在这里不相关。