Entfernen Sie ✅, 🔥, ✈, ♛ und andere solche Emojis / Bilder / Zeichen aus Java-Strings

192

Ich habe einige Zeichenfolgen mit allen möglichen Emojis / Bildern / Zeichen.

Nicht alle Zeichenfolgen sind in Englisch - einige von ihnen sind in anderen nicht-lateinischen Sprachen, zum Beispiel:

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

... und viele mehr davon.

Ich möchte all diese Zeichen / Bilder loswerden und nur die Buchstaben (und Satzzeichen) in den verschiedenen Sprachen behalten.

Ich habe versucht, die Schilder mit der EmojiParser-Bibliothek zu reinigen :

String withoutEmojis = EmojiParser.removeAllEmojis(input);

Das Problem ist, dass EmojiParser die meisten Zeichen nicht entfernen kann. Das ♦ Zeichen ist das einzige, das ich bis jetzt gefunden habe, als es entfernt wurde. Andere Zeichen wie ✪ ❉ ★ ✰ ❈ ❧ ❧ ✂ ❋ ⓡ ✿ not not werden nicht entfernt.

Gibt es eine Möglichkeit, alle diese Zeichen aus den Eingabezeichenfolgen zu entfernen und nur die Buchstaben und Satzzeichen in den verschiedenen Sprachen beizubehalten?

riorio
quelle
91
was willst du behalten
YCF_L
31
Zwei Probleme: Was ist EmojiParser? Scheint nicht Teil einer Standardbibliothek zu sein, daher ist diese Erwähnung nicht sehr hilfreich. Und welche Zeichen möchten Sie genau filtern? Sie sagen "viel mehr von dieser Art", aber es gibt viele Charaktergruppen und Familien. Wir müssen mehr über Ihre Kriterien wissen.
Markus Fischer
129
IDK, was Ihre Motive dahinter sind, aber wenn es zu filtrige Texteingabe ist: nicht. Ich bin es leid, gezwungen zu werden, a-zA-Z zu verwenden. Lassen Sie mich in meiner Muttersprache oder Emojis schreiben oder was auch immer ich will. Soll mein Kalendertermin wirklich "🤦🏻‍♂️" heißen? Ja ja mache ich. Jetzt geh mir aus dem Weg.
Alexander - Reinstate Monica
19
Bitte klären Sie, was genau Sie behalten und entfernen möchten. An der Oberfläche scheint die Frage klar zu sein, aber aufgrund der Komplexität von Unicode ist dies nicht der Fall und aus diesem Grund ist es unmöglich, eine gute Antwort zu geben.
Oleg
12
Das scheint eine seltsame Sache zu sein, wenn es die Bedeutung von mindestens einem Ihrer Beispiele zerstört.
Eevee

Antworten:

290

Wie wäre es, eine Whitelist der Charaktere zu erstellen, die Sie behalten möchten, anstatt einige Elemente auf die schwarze Liste zu setzen? Auf diese Weise müssen Sie sich nicht um jedes neue Emoji kümmern, das hinzugefügt wird.

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

So:

  • [\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]ist ein Bereich, der alle numerischen ( \\p{N}), Buchstaben ( \\p{L}), Markierungen ( \\p{M}), Satzzeichen ( \\p{P}), Leerzeichen / Trennzeichen ( \\p{Z}), anderen Formatierungszeichen ( \\p{Cf}) und anderen Zeichen oben U+FFFFin Unicode ( \\p{Cs}) und Zeilenumbrüche ( \\s) darstellt. Enthält \\p{L}speziell die Zeichen aus anderen Alphabeten wie Kyrillisch, Latein, Kanji usw.
  • Der ^im regulären Ausdruck enthaltene Zeichensatz negiert die Übereinstimmung.

Beispiel:

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 _# 皆さん、こんにちは! 私はジョンと申します。"

Wenn Sie weitere Informationen benötigen, lesen Sie die Java- Dokumentation für reguläre Ausdrücke.

Nick Bull
quelle
4
Die offensichtliche Lücke zwischen alphanumerischen ASCII-Zeichen und Emoji besteht aus akzentuierten und nicht lateinischen Buchstaben. Ohne die Eingabe des OP zu diesen wissen wir nicht, ob dies eine gute Antwort ist (aber nicht mein DV)
Chris H
4
Ja, ich bin gespannt, warum dies möglicherweise abgelehnt wird. Das zweite Mal, als ich diese Frage sah, war ein regulärer Ausdruck das absolut erste, was mir in den Sinn kam (PS, da er nach Standardzeichen und Interpunktion sucht, würde ich so etwas verwenden, [^\w\^\-\[\]\.!@#$%&*\(\)/+'":;~?,]aber ich bin nur robust und versuche, alle typischen Zeichen zu sammeln, die es nicht gibt keine Symbole). Upvoted, weil dies definitiv eine mögliche Lösung ist. Wenn er andere Sprachzeichen hinzufügen möchte, kann er diese nach Bedarf zum Ausdruck hinzufügen.
Chris
15
@ Chris tolles Interpunktions-Regex-Beispiel, sieht für mich in einigen Fällen umfangreich genug aus. Vielleicht lesen die Leute dann auch nicht die ganze Antwort - wie am Ende der Antwort angegeben, werden p{L}nicht-englische alphabetische Zeichen behandelt . Ich hoffe, es versteht sich, dass ich in meiner Antwort nicht jedes nicht-englische Alphabet ausführlich auflisten kann, da dies unpraktisch ausführlich wäre.
Nick Bull
12
Dies. Bitte und Danke. Versuchen Sie nicht, Zeichen zu verbieten , die Ihnen Probleme bereiten. Entscheiden Sie, welche Zeichen Sie zulassen, und codieren Sie diese. Dann hat Ihr Code einen klar definierten Satz von Testfällen.
jpmc26
2
Ich schlage vor "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\s]". Dies ermöglicht die allgemeinen Kategorien Buchstaben, Markierungen, Zahlen, Interpunktion, Trennzeichen und "Andere, Format" sowie Leerzeichen wie Tabulatoren und Zeilenumbrüche.
Sean Van Gorder
81

Ich mag Java nicht besonders, daher werde ich nicht versuchen, Beispielcode inline zu schreiben, aber ich würde dies tun, um zu überprüfen, was Unicode "die allgemeine Kategorie" jedes Zeichens nennt. Es gibt einige Buchstaben- und Interpunktionskategorien.

Mit Character.getType können Sie die allgemeine Kategorie eines bestimmten Zeichens ermitteln. Sie sollten wahrscheinlich die Zeichen behalten, die in diese allgemeinen Kategorien fallen:

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

(Alle Zeichen, die Sie als speziell entfernen möchten, haben eine allgemeine Kategorie OTHER_SYMBOL, die ich nicht in die Whitelist der obigen Kategorie aufgenommen habe.)

Daniel Wagner
quelle
1
FORMAT (vgl.) Sollte ebenfalls erhalten bleiben; Dies schließt die Cluster- und Richtungsüberschreibungen ein, ohne die es unmöglich ist, bestimmte (zugegebenermaßen ungewöhnliche) Wörter in einigen Sprachen zu schreiben.
zwol
@zwol Danke für die Details! Ich werde es der Liste hinzufügen.
Daniel Wagner
29
Dies ist die zukunftssichere Antwort. Unabhängig von zukünftigen Aktualisierungen des Unicode-Standards bedeutet das Einschließen / Ausschließen von Zeichen basierend auf ihren Kategorien, dass das individuelle Parsen von Zeichen und die Pflege einer Liste nicht erforderlich ist. Natürlich sollten flüchtige Tests von Text in verschiedenen Sprachen (z. B. Chinesisch, Arabisch usw.) durchgeführt werden, um sicherzustellen, dass die gefilterten Kategorien mit dem Text übereinstimmen, der in der Zielumgebung zulässig sein muss.
CJBS
3
Oh, ein weiteres Problem, an das ich gestern hätte denken sollen: TAB, CR und LF sind alle allgemeine Kategorien Cc (Java's CONTROL). Diese müssen speziell in die Whitelist aufgenommen werden, da Sie mit ziemlicher Sicherheit die meisten älteren Steuerzeichen nicht zulassen möchten.
zwol
@CJBS Das Problem bei diesem Ansatz ist, dass er nur teilweise in Java implementiert wurde. Sie erfahren beispielsweise Character.getType()nicht, ob es sich bei Ihrem char(oder intCodepunkt, da die Methode überladen ist) beispielsweise um ein Emoticon, ein Musiksymbol, ein Emoji-Zeichen usw. handelt. Wenn Sie einen einfachen Anwendungsfall haben, ist dies möglicherweise in Ordnung Diesen Weg zu gehen - es ist sicherlich ein eleganter Ansatz, der leicht zu verstehen ist -, aber seien Sie sich bewusst, dass er brechen kann, wenn sich die Anforderungen ändern.
Skomisa
47

Basierend auf der vollständigen Emoji-Liste, Version 11.0 , müssen 1644 verschiedene Unicode-Codepunkte entfernt werden. Zum Beispiel steht auf dieser Liste als U+2705.

Wenn Sie die vollständige Liste der Emojis haben, müssen Sie sie mithilfe von Codepunkten herausfiltern . Das Iterieren über einzelne charoder bytenicht als einzelner Codepunkt kann mehrere Bytes umfassen. Da Java UTF-16-Emojis verwendet, dauert es normalerweise zwei charSekunden.

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

Die Zuordnung vom Unicode-Codepunkt U+2705zu Java intist unkompliziert:

int viSign = 0x2705;

oder da Java Unicode-Zeichenfolgen unterstützt:

int viSign = "✅".codePointAt(0);
Karol Dowbecki
quelle
28
Sehr nützliche Liste. Interessant, dass etwas namens EmojiParser mit einer Methode namens removeAllEmojis diese nicht handhabt ... :-)
TJ Crowder
7
@Bergi: Nein, da input.codePointAtnur maximal 2 Zeichen angezeigt werden, was eine konstante Obergrenze ist. Außerdem i += Character.charCount(cp)überspringt (das neu hinzugefügte) alle input.codePointAtgeprüften Zeichen (in einigen Eckfällen minus 1).
David Foerster
6
@ OlivierGrégoire: String.chars()Streams über Zeichen, nicht über Codepunkte. Dafür gibt es eine separate Methode String.codePoints().
David Foerster
5
Hier gibt es mindestens zwei Probleme: Sie verwenden eine "geschlossene" Liste von Emojis, daher müssen Sie sie jedes Jahr erweitern (dies ist jedoch wahrscheinlich nicht leicht lösbar), und dieser Code funktioniert wahrscheinlich nicht richtig mit Codepunktsequenzen (siehe zum Beispiel unicode.org/Public/emoji/11.0/emoji-zwj-sequences.txt )
xanatos
49
Dies ist im Grunde der gleiche Ansatz wie bei EmojiParser und wird aus demselben Grund bald fehlschlagen. Neue Emojis werden relativ häufig zur Unicode-Zeichendatenbank hinzugefügt. Wenn Sie jetzt eine Lösung mit den derzeit definierten 1644-Emojis für einen negativen Regelsatz implementieren, schlägt die Implementierung fehl, sobald neue Emojis verfügbar werden.
jarnbjo
20

ICU4J ist dein Freund.

UCharacter.hasBinaryProperty(UProperty.EMOJI);

Denken Sie daran, Ihre Version von icu4j auf dem neuesten Stand zu halten, und beachten Sie, dass dadurch nur offizielle Unicode-Emoji herausgefiltert werden, keine Symbolzeichen. Kombinieren Sie dies wie gewünscht mit dem Herausfiltern anderer Zeichentypen.

Weitere Informationen: http://icu-project.org/apiref/icu4j/com/ibm/icu/lang/UProperty.html#EMOJI

Daniel F.
quelle
1
Bis Java so aktualisiert wird, dass es die binäre Emoji-Eigenschaft enthält, wäre dies eine gute Lösung. Die Bibliothek muss jedoch häufig für die neu hinzugefügten Codepunkte aktualisiert werden.
nhahtdh
10

Ich habe unten einige Beispiele gegeben und dachte, dass Latein genug ist, aber ...

Gibt es eine Möglichkeit, alle diese Zeichen aus der Eingabezeichenfolge zu entfernen und nur die Buchstaben und Satzzeichen in den verschiedenen Sprachen beizubehalten?

Entwickelte nach der Bearbeitung eine neue Lösung unter Verwendung der Character.getTypeMethode, und das scheint der beste Schuss zu sein.

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

}

Ausgabe:

---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
早上好 
早上好 
Καλημέρα 
Καλημέρα 

Der Code funktioniert durch Streaming des Strings zu Codepunkten. Verwenden Sie dann Lambdas, um Zeichen in a zu filternint Array , und konvertieren Sie das Array in String.

Die Buchstaben und Leerzeichen werden mit den Zeichenmethoden gefiltert, was bei der Interpunktion nicht gut ist. Versuch fehlgeschlagen .

Der Unicode blockiert den Weißfilter unter Verwendung der Unicode-Blöcke, die der Programmierer als zulässig angibt. Versuch fehlgeschlagen .

Der Unicode blockiert den Schwarzfilter unter Verwendung der Unicode-Blöcke, die der Programmierer als nicht zulässig angibt. Versuch fehlgeschlagen .

Der Kategoriefilter mit der statischen Methode Character.getType. Der Programmierer kann im categoryArray definieren, welche Typen zulässig sind. WERKE 😨😱😰😲😀.

Marcos Zolnowski
quelle
import java.lang.Character.UnicodeBlock;dann Character.UnicodeBlock-> UnicodeBlock.
Bernhard Barker
Alle Ihre Wege haben die Tests nicht bestanden.
Oleg
@Oleg nein, schau nochmal, das white listBeispiel.
Marcos Zolnowski
Irgendwas muss mit meinen Augen oder meinem Monitor nicht stimmen, ich kann nicht sehen, ist 早上 好 und Καλημέρα
Oleg
4
Beachten Sie, dass die Java-Sprache etwas langsamer ist und neuere Unicode-Versionen unterstützt ... Zum Beispiel unterstützt Java 10 nur Unicode 8 (daher beschreiben seine Zeichenklassen nur Unicode 8-Zeichen) ... So viele Emojis sind nicht vorhanden (siehe docs.oracle) .com / javase / 10 / docs / api / java / lang / Character.html , Die
Zeicheninformationen
0

Probieren Sie dieses Projekt aus simple-emoji-4j

Kompatibel mit Emoji 12.0 (2018.10.15)

Einfach mit:

EmojiUtils.removeEmoji(str)
Liheyuan
quelle
-1

Verwenden Sie ein jQuery-Plugin namens RM-Emoji. So funktioniert das:

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

Dies ist der schnelle Modus, in dem möglicherweise einige Emojis fehlen, da heuristische Algorithmen zum Auffinden von Emojis im Text verwendet werden. Verwenden Sie die .full()Methode, um die gesamte Zeichenfolge zu scannen und alle garantierten Emojis zu entfernen.

Atwood Mandelbrot-Spolsky
quelle
5
Die Frage war in Java, daher ist ein jQuery-Plugin hier nicht relevant.
Riorio