Rimuovi ✅, 🔥, ✈, ♛ e altri emoji / immagini / segni simili dalle stringhe Java


192

Ho alcune stringhe con tutti i tipi di emoji / immagini / segni diversi.

Non tutte le stringhe sono in inglese - alcune sono in altre lingue non latine, ad esempio:

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

... e molti altri ancora.

Vorrei sbarazzarmi di tutti questi segni / immagini e mantenere solo le lettere (e la punteggiatura) nelle diverse lingue.

Ho provato a pulire i segni usando la libreria EmojiParser :

String withoutEmojis = EmojiParser.removeAllEmojis(input);

Il problema è che EmojiParser non è in grado di rimuovere la maggior parte dei segni. Il segno ♦ è l'unico che ho trovato fino ad ora che è stato rimosso. Altri segni come ✪ ❉ ★ ✰ ❈ ❧ ✂ ❋ ⓡ ✿ ♛ 🔥 non vengono rimossi.

C'è un modo per rimuovere tutti questi segni dalle stringhe di input e mantenere solo le lettere e la punteggiatura nelle diverse lingue ?


91
cosa vuoi conservare?
YCF_L

31
Due problemi: che cos'è EmojiParser? Non sembra far parte di una libreria standard, quindi questa menzione non è molto utile. E quali personaggi vuoi esattamente filtrare? Dici "molti altri di questo tipo", ma ci sono molti gruppi di personaggi e famiglie. Dobbiamo sapere di più sui tuoi criteri.
Markus Fischer,

129
IDK quali sono le tue motivazioni dietro questo, ma se è troppo un filtro di input di testo: non farlo. Sono stanco di essere costretto a usare a-zA-Z. Lasciami scrivere nella mia lingua madre, o emoji o qualunque cosa io voglia. Voglio davvero che il mio appuntamento del calendario sia chiamato "🤦🏻‍♂️"? Si si lo faccio. Adesso togliti di mezzo.
Alexander - Ripristina Monica il

19
Si prega di chiarire che cosa esattamente si desidera conservare e rimuovere. In apparenza la domanda sembra essere chiara ma a causa della complessità di Unicode non lo è e per questo è impossibile fornire una buona risposta.
Oleg,

12
sembra una cosa strana voler fare quando distrugge il significato di almeno uno dei tuoi esempi?
Eevee,

Risposte:


290

Invece di inserire nella blacklist alcuni elementi, che ne dici di creare una lista bianca dei personaggi che desideri conservare? In questo modo non devi preoccuparti di ogni nuova emoji aggiunta.

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

Così:

  • [\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]è un intervallo che rappresenta tutti i caratteri numerici ( \\p{N}), letter ( \\p{L}), mark ( \\p{M}), punteggiatura ( \\p{P}), spazi bianchi / separatore ( \\p{Z}), altra formattazione ( \\p{Cf}) e altri caratteri sopra U+FFFFin Unicode ( \\p{Cs}) e newline ( \\s) caratteri. include in \\p{L}particolare i caratteri di altri alfabeti come cirillico, latino, kanji, ecc.
  • Il ^set di caratteri regex annulla la corrispondenza.

Esempio:

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

Se hai bisogno di ulteriori informazioni, consulta la documentazione Java per regex.


4
L'ovvio divario tra caratteri alfanumerici ASCII ed emoji è accentato e lettere non latine. Senza il contributo del PO su questi non sappiamo se questa è una buona risposta (non il mio DV però)
Chris H

4
Sì, sono curioso di sapere perché questo potrebbe essere retrocesso. La seconda che ho visto questa domanda, un'espressione regolare è stata la prima cosa in assoluto che è venuto in mente (PS da quando è alla ricerca di caratteri standard e punteggiatura, userei qualcosa di simile [^\w\^\-\[\]\.!@#$%&*\(\)/+'":;~?,], ma questo è solo me che sono robusti e cercando raccogliere tutti i caratteri tipici che aren 't simboli). È stato votato perché questa è sicuramente una potenziale soluzione. Se desidera aggiungere altri caratteri linguistici, può aggiungerli all'espressione se necessario.
Chris,

15
@Chris ottimo esempio regex di punteggiatura, mi sembra abbastanza esteso per alcuni casi. Inoltre, forse le persone non stanno leggendo l'intera risposta allora - come indicato nella parte inferiore della risposta, p{L}gestisce caratteri alfabetici non inglesi . Spero che sia chiaro che non posso elencare ampiamente tutti gli alfabeto non inglesi nella mia risposta in quanto ciò sarebbe impraticabilmente dettagliato.
Nick Bull,

12
Questo. Per favore e grazie. Non cercare di vietare i personaggi che ti causano problemi; decidere quali caratteri consentire e codificarlo. Quindi il tuo codice ha una serie chiaramente definita di casi di test.
jpmc26,

2
Io suggerisco "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\s]". Ciò consente le categorie generali Letter, Mark, Number, Punctuation, Separator e "Other, Format", nonché i caratteri di spazi bianchi come tab e newline.
Sean Van Gorder,

81

Non sono super in Java, quindi non proverò a scrivere un esempio di codice in linea, ma il modo in cui lo farei è quello di controllare ciò che Unicode chiama "la categoria generale" di ogni personaggio. Ci sono una coppia di lettere e categorie di punteggiatura.

Puoi usare Character.getType per trovare la categoria generale di un determinato personaggio. Probabilmente dovresti conservare quei personaggi che rientrano in queste categorie generali:

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

(Tutti i personaggi che hai elencato come specificamente desiderati da rimuovere hanno una categoria generale OTHER_SYMBOL, che non ho incluso nella lista bianca delle categorie sopra.)


1
FORMATO (Cf) dovrebbe essere preservato anche; questo include il raggruppamento e le sostituzioni direzionali, senza le quali è impossibile scrivere certe parole (inusuali, certo) in alcune lingue.
zwol,

@zwol Grazie per i dettagli! Lo aggiungerò alla lista.
Daniel Wagner,

29
Questa è la risposta a prova di futuro. Indipendentemente dai futuri aggiornamenti dello standard Unicode, includere / escludere i caratteri in base alle loro categorie significa che l'analisi individuale dei caratteri e il mantenimento di un elenco non sono necessari. Naturalmente, è necessario eseguire test rapidi di testo in diverse lingue (ad esempio cinese, arabo, ecc.) Per garantire che le categorie filtrate corrispondano al testo richiesto per essere consentito nell'ambiente di destinazione.
CJBS,

3
Oh, un altro gotcha a cui avrei dovuto pensare ieri: TAB, CR e LF sono tutti di categoria generale Cc (CONTROLLO di Java). Questi devono essere appositamente autorizzati, dal momento che quasi sicuramente non vuoi consentire la maggior parte dei personaggi di controllo legacy.
zwol,

@CJBS Il problema con questo approccio è che è stato implementato solo parzialmente in Java. Ad esempio, Character.getType()non ti dirò se il tuo char(o intpunto di codice poiché il metodo è sovraccarico) è, per esempio, un'emoticon, un simbolo musicale, o un personaggio emoji, ecc. Se hai un semplice caso d'uso potrebbe andare bene percorrere questa strada - è certamente un approccio elegante che è facile da capire - ma attenzione che potrebbe rompersi se i requisiti cambiassero.
skomisa,

47

Sulla base dell'Elenco Emoji completo, v11.0 hai 1644 punti di codice Unicode diversi da rimuovere. Ad esempio è in questo elenco comeU+2705 .

Avendo l'elenco completo di emoji è necessario filtrarli utilizzando i punti di codice . L'iterazione su singolo charo bytenon funziona come un singolo punto di codice può estendersi su più byte. Poiché Java utilizza gli emoji UTF-16 di solito impiegheranno due charsecondi.

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

La mappatura dal punto di codice Unicode U+2705a Java intè semplice:

int viSign = 0x2705;

o poiché Java supporta le stringhe Unicode:

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

28
Elenco molto utile Interessante che qualcosa chiamato EmojiParser con un metodo chiamato removeAllEmojis non riesca a gestire questi ... :-)
TJ Crowder

7
@Bergi: No, dato input.codePointAtche guarda al massimo fino a 2 caratteri che è un limite superiore costante. Inoltre (il nuovo aggiunto) i += Character.charCount(cp)ignora tutti i personaggi che hanno input.codePointAtesaminato (meno 1 in alcuni casi d'angolo).
David Foerster,

6
@ OlivierGrégoire: String.chars()flussi su caratteri non punti di codice. C'è un metodo separato String.codePoints()per quello.
David Foerster,

5
Ci sono almeno due problemi qui: stai usando un elenco "chiuso" di emoji, quindi ogni anno devi estenderlo (ma questo probabilmente non è facilmente risolvibile), e questo codice probabilmente non funzionerà correttamente con le sequenze di codici (vedi ad esempio unicode.org/Public/emoji/11.0/emoji-zwj-sequences.txt )
xanatos

49
Questo è fondamentalmente lo stesso approccio usato da EmojiParser e presto fallirà per lo stesso motivo. Nuovi emoji vengono aggiunti frequentemente al database dei caratteri Unicode e se stai implementando una soluzione utilizzando gli emoji 1644 attualmente definiti per un set di regole negativo, l'implementazione fallirà non appena saranno disponibili nuovi emoji.
jarnbjo,

20

ICU4J è tuo amico.

UCharacter.hasBinaryProperty(UProperty.EMOJI);

Ricorda di mantenere aggiornata la tua versione di icu4j e nota che questo filtrerà solo le emoji Unicode ufficiali, non i simboli. Combina con il filtraggio di altri tipi di carattere, se lo desideri.

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


1
Fino a quando Java non verrà aggiornato per includere la proprietà binaria Emoji, suppongo che questa sarebbe una buona soluzione. Tuttavia, la libreria deve essere aggiornata spesso per i punti di codice appena aggiunti.
nhahtdh,

10

Di seguito ho fornito alcuni esempi e ho pensato che il latino fosse abbastanza, ma ...

C'è un modo per rimuovere tutti questi segni dalla stringa di input e mantenere solo le lettere e la punteggiatura nelle diverse lingue?

Dopo la modifica, ho sviluppato una nuova soluzione, usando il Character.getTypemetodo, e sembra essere la soluzione migliore.

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

}

Produzione:

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

Il codice funziona eseguendo lo streaming della stringa in punti di codice. Quindi usando lambdas per filtrare i caratteri in aint array, quindi convertiamo l'array in String.

Le lettere e gli spazi utilizzano i metodi Carattere per filtrare, non bene con la punteggiatura. Tentativo fallito .

L' unicode blocca il filtro bianco utilizzando i blocchi unicode che il programmatore specifica come consentito. Tentativo fallito .

L' unicode blocca il filtro nero usando i blocchi unicode che il programmatore specifica come non consentito. Tentativo fallito .

Il filtro di categoria usando il metodo statico Character.getType. Il programmatore può definire categorynell'array quali tipi sono consentiti. LAVORI 😨😱😰😲😀.


import java.lang.Character.UnicodeBlock;, quindi Character.UnicodeBlock-> UnicodeBlock.
Bernhard Barker,

Tutti i tuoi modi hanno fallito i test.
Oleg,

@Oleg no, guarda di nuovo, l' white listesempio.
Marcos Zolnowski,

Qualcosa deve essere sbagliato nei miei occhi o nel mio monitor, non riesco a vedere è 早上 好 e Καλημέρα
Oleg

4
Nota che il linguaggio Java è un po 'lento e supporta le versioni Unicode più recenti ... Ad esempio Java 10 supporta solo Unicode 8 (quindi le sue classi di caratteri descrivono solo caratteri Unicode 8) ... Quindi molti emoji non sono presenti (vedi docs.oracle .com / javase / 10 / docs / api / java / lang / Character.html , Le informazioni sui personaggi si basano sullo standard Unicode, versione 8.0.0. )
xanatos


-2

Utilizzare un plug-in jQuery chiamato RM-Emoji. Ecco come funziona:

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

Questa è la modalità veloce a cui potrebbero mancare alcuni emoji in quanto utilizza algoritmi euristici per trovare emoji nel testo. Usa il .full()metodo per scansionare l'intera stringa e rimuovere tutti gli emoji garantiti.


5
La domanda era in Java, quindi un plugin jQuery non è rilevante qui.
riorio,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.