Usuń ✅, 🔥, ✈, ♛ i inne takie emoji / obrazy / znaki z ciągów Java

192

Mam kilka ciągów znaków z różnymi rodzajami emoji / obrazów / znaków.

Nie wszystkie ciągi są w języku angielskim - niektóre z nich są w innych językach niełacińskich, na przykład:

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

... i wiele innych.

Chciałbym pozbyć się wszystkich tych znaków / obrazów i zachować tylko litery (i znaki interpunkcyjne) w różnych językach.

Próbowałem wyczyścić znaki przy użyciu biblioteki EmojiParser :

String withoutEmojis = EmojiParser.removeAllEmojis(input);

Problem polega na tym, że EmojiParser nie jest w stanie usunąć większości znaków. Znak ♦ jest jedynym, jaki do tej pory znalazłem, kiedy go usunąłem. Inne znaki, takie jak ✪ ❉ ★ ✰ ❈ ❧ ✂ ❋ ⓡ ✿ ♛ 🔥, nie są usuwane.

Czy istnieje sposób na usunięcie wszystkich tych znaków z ciągów wejściowych i zachowanie tylko liter i interpunkcji w różnych językach ?

riorio
źródło
91
co chcesz zachować?
YCF_L
31
Dwa problemy: Co to jest EmojiParser? Nie wydaje się być częścią standardowej biblioteki, więc ta wzmianka nie jest zbyt pomocna. A jakie dokładnie postacie chcesz filtrować? Mówisz „dużo więcej tego rodzaju”, ale istnieje wiele grup postaci i rodzin. Musimy dowiedzieć się więcej o twoich kryteriach.
Markus Fischer
129
IDK, jakie są twoje motywacje, ale jeśli to zbyt filtrowanie tekstu: nie. Mam dość zmuszania mnie do używania a-zA-Z. Pozwól mi pisać w moim ojczystym języku, emoji lub cokolwiek chcę. Czy naprawdę chcę, aby spotkanie w kalendarzu nazywało się „🤦🏻‍♂️”? Tak, tak. Teraz zejdź mi z drogi.
Alexander - Przywróć Monikę
19
Wyjaśnij, co dokładnie chcesz zachować i usunąć. Z pozoru pytanie wydaje się jasne, ale ze względu na złożoność Unicode tak nie jest, dlatego nie można udzielić dobrej odpowiedzi.
Oleg
12
wydaje się to dziwną rzeczą, gdy chcesz zniszczyć znaczenie co najmniej jednego z twoich przykładów?
Eevee

Odpowiedzi:

290

Zamiast umieszczać na czarnej liście niektóre elementy, co powiesz na utworzenie białej listy znaków, które chcesz zachować? W ten sposób nie musisz się martwić dodawaniem nowych emoji.

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

Więc:

  • [\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]to zakres reprezentujący wszystkie cyfry ( \\p{N}), literę ( \\p{L}), znak ( \\p{M}), interpunkcję ( \\p{P}), białe znaki / separator ( \\p{Z}), inne formatowanie ( \\p{Cf}) i inne powyższe znaki U+FFFFw Unicode ( \\p{Cs}) i \\sznak nowej linii ( ). \\p{L}w szczególności obejmuje znaki z innych alfabetów, takich jak cyrylica, łacina, kanji itp.
  • Zestaw ^znaków wyrażenia regularnego neguje dopasowanie.

Przykład:

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

Jeśli potrzebujesz więcej informacji, zapoznaj się z dokumentacją Java dla wyrażeń regularnych.

Nick Bull
źródło
4
Oczywistą luką między znakami alfanumerycznymi ASCII a emoji są akcentowane i niełacińskie litery. Bez wkładu OP w te kwestie nie wiemy, czy to dobra odpowiedź (choć nie moja DV)
Chris H
4
Tak, jestem ciekawy, dlaczego to mogłoby zostać odrzucone. Gdy tylko zobaczyłem to pytanie, wyrażenie regularne było absolutnie pierwszą rzeczą, która przyszła mi do głowy (PS, ponieważ szuka standardowych znaków i interpunkcji, użyłbym czegoś takiego, [^\w\^\-\[\]\.!@#$%&*\(\)/+'":;~?,]ale to po prostu jestem silny i próbuję zebrać wszystkie typowe znaki, które nie są symbole t). Pozytywnie oceniany, ponieważ jest to z pewnością potencjalne rozwiązanie. Jeśli chce dodać inne znaki języka, może w razie potrzeby dodać je do wyrażenia.
Chris
15
@Chris świetny przykład wyrażenia regularnego interpunkcji, w niektórych przypadkach wygląda na wystarczająco obszerny. Być może ludzie nie czytają wtedy całej odpowiedzi - jak podano na dole odpowiedzi, p{L}obsługuje nieangielskie znaki alfabetyczne . Mam nadzieję, że zrozumiałe jest, że nie potrafię wyczerpująco wypisać wszystkich alfabetów nieanglojęzycznych w mojej odpowiedzi, ponieważ byłoby to niepraktycznie gadatliwe.
Nick Bull
12
To. Proszę i dziękuję. Nie próbuj zabraniać znaków, które powodują problemy; zdecyduj, jakie znaki zezwalasz i koduj to. Wówczas twój kod ma jasno zdefiniowany zestaw przypadków testowych.
jpmc26
2
Sugeruję "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\s]". Pozwala to na kategorie ogólne Litera, Znak, Liczba, Interpunkcja, Separator i „Inne, Format”, a także znaki spacji, takie jak tabulator i znak nowej linii.
Sean Van Gorder
81

Nie przepadam za Javą, więc nie będę próbował pisać przykładowego kodu inline, ale sposób, w jaki to zrobię, to sprawdzenie, co Unicode nazywa „kategorią ogólną” każdego znaku. Istnieje kilka liter i kategorii interpunkcyjnych.

Możesz użyć Character.getType, aby znaleźć ogólną kategorię danego znaku. Prawdopodobnie powinieneś zachować te postacie, które należą do tych ogólnych kategorii:

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

(Wszystkie postacie, które chciałeś usunąć, mają kategorię ogólną OTHER_SYMBOL, której nie znalazłem na białej liście powyższych kategorii).

Daniel Wagner
źródło
1
FORMAT (Cf) również powinien zostać zachowany; obejmuje to grupowanie i zastępowanie kierunkowe, bez których niemożliwe jest napisanie niektórych (niezwykłych, co prawda) słów w niektórych językach.
zwolnienie
@zwol Dzięki za szczegóły! Dodam to do listy.
Daniel Wagner
29
To jest odpowiedź na przyszłość. Niezależnie od przyszłych aktualizacji standardu Unicode, w tym / wykluczanie znaków na podstawie ich kategorii oznacza, że ​​indywidualne parsowanie znaków i utrzymywanie listy nie jest konieczne. Oczywiście należy pobieżnie przetestować tekst w różnych językach (np. Chiński, arabski itp.), Aby upewnić się, że filtrowane kategorie pasują do tekstu wymaganego w środowisku docelowym.
CJBS
3
Aha, inna myśl, o której powinienem pomyśleć wczoraj: TAB, CR i LF są kategorią ogólną Cc (CONTROL Java). Te muszą być specjalnie umieszczone na białej liście, ponieważ prawie na pewno nie chcesz zezwalać na większość starszych postaci kontrolnych.
zwolnienie
@CJBS Problem z tym podejściem polega na tym, że został on tylko częściowo zaimplementowany w Javie. Na przykład Character.getType()nie powie ci, czy twój char(lub intpunkt kodowy od momentu przeciążenia metody) jest, powiedzmy, emotikonem, symbolem muzycznym, znakiem emoji itp. Jeśli masz prosty przypadek użycia, może być w porządku pójść tą ścieżką - to z pewnością eleganckie i łatwe do zrozumienia podejście - ale pamiętaj, że może się złamać, jeśli zmienią się wymagania.
skomisa
47

W oparciu o pełną listę emotikonów wer. 11.0 masz 1644 różnych punktów kodu Unicode do usunięcia. Na przykład jest na tej liście jako U+2705.

Mając pełną listę emotikonów, musisz je odfiltrować za pomocą punktów kodowych . Iteracja po pojedynczym charlub bytenie będzie działać, ponieważ pojedynczy punkt kodowy może obejmować wiele bajtów. Ponieważ Java używa emoji UTF-16 zwykle zajmuje dwa 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); 
}

Mapowanie z punktu kodu Unicode U+2705na Javę intjest proste:

int viSign = 0x2705;

lub ponieważ Java obsługuje ciągi Unicode:

int viSign = "✅".codePointAt(0);
Karol Dowbecki
źródło
28
Bardzo przydatna lista. Ciekawe, że coś o nazwie EmojiParser z metodą o nazwie removeAllEmojis nie obsługuje tych ... :-)
TJ Crowder
7
@Bergi: Nie, ponieważ input.codePointAtpatrzy tylko na maksymalnie 2 znaki, co stanowi stałą górną granicę. Ponadto (nowo dodane) i += Character.charCount(cp)pomija wszystkie postacie, które input.codePointAtsprawdzono (minus 1 w niektórych przypadkach narożnych).
David Foerster
6
@ OlivierGrégoire: String.chars()przesyła strumieniowo znaki, a nie znaki kodowe. Jest na to osobna metoda String.codePoints().
David Foerster
5
Występują tutaj co najmniej dwa problemy: używasz „zamkniętej” listy emoji, więc co roku musisz ją rozszerzać (ale prawdopodobnie nie jest to łatwe do rozwiązania), a ten kod prawdopodobnie nie będzie działał poprawnie z sekwencjami punktów kodowych (patrz na przykład unicode.org/Public/emoji/11.0/emoji-zwj-sequences.txt )
Xanatos
49
Jest to w zasadzie takie samo podejście, jak w EmojiParser i wkrótce zakończy się niepowodzeniem z tego samego powodu. Nowe emoji są stosunkowo często dodawane do bazy znaków Unicode, a jeśli teraz wdrażasz rozwiązanie wykorzystujące obecnie zdefiniowane emoji 1644 dla zestawu reguł negatywnych, wdrożenie zakończy się niepowodzeniem, gdy tylko nowe emoji będą dostępne.
jarnbjo
20

ICU4J jest twoim przyjacielem.

UCharacter.hasBinaryProperty(UProperty.EMOJI);

Pamiętaj, aby aktualizować swoją wersję icu4j i pamiętaj, że odfiltruje ona tylko oficjalne emoji Unicode, a nie znaki symboli. Połącz z filtrowaniem innych typów znaków według potrzeb.

Więcej informacji: http://icu-project.org/apiref/icu4j/com/ibm/icu/lang/UProperty.html#EMOJI

Daniel F.
źródło
1
Dopóki Java nie zostanie zaktualizowana o własność binarną Emoji, myślę, że byłoby to dobre rozwiązanie. Jednak biblioteka musi być często aktualizowana dla nowo dodanych punktów kodowych.
nhahtdh
10

Podałem kilka przykładów poniżej i pomyślałem, że łacina wystarczy, ale ...

Czy istnieje sposób na usunięcie wszystkich tych znaków z ciągu wejściowego i zachowanie tylko liter i interpunkcji w różnych językach?

Po edycji opracowałem nowe rozwiązanie, korzystając z Character.getTypemetody, i wydaje się, że jest to najlepsze ujęcie.

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

}

Wynik:

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

Kod działa poprzez przesyłanie strumienia do punktów kodowych. Następnie za pomocą lambda do filtrowania znaków wint tablicy, a następnie przekonwertujemy tablicę na String.

Te litery i spacje korzysta z wykorzystaniem metod znak do filtra, nie jest dobra ze znaków interpunkcyjnych. Nieudana próba .

Te bloki Unicode białe filtry wykorzystujące bloków Unicode określa się jako programista dozwolone. Nieudana próba .

Bloków Unicode czarny filtr używając bloków Unicode określa się jako programista nie dozwolony. Nieudana próba .

Kategoria filtr stosując metodę statyczną Character.getType. Programista może określić w categorytablicy, jakie typy są dozwolone. PRACE 😨😱😰😲😀.

Marcos Żołnowski
źródło
import java.lang.Character.UnicodeBlock;, a następnie Character.UnicodeBlock-> UnicodeBlock.
Bernhard Barker
Wszystkie twoje drogi zawiodły testy.
Oleg
@Oleg nie, spójrz jeszcze raz, white listprzykład.
Marcos Żołnowski
Coś musi być nie tak z moimi oczami lub monitorem, nie widzę 早上 好 i Καλημέρα
Oleg
4
Zauważ, że język Java jest trochę powolny, obsługując nowsze wersje Unicode ... Na przykład Java 10 obsługuje tylko Unicode 8 (więc jego klasy znaków opisują tylko znaki Unicode 8) ... Tak wiele emoji nie jest prezentowanych (patrz docs.oracle .com / javase / 10 / docs / api / java / lang / Character.html , Informacje o postaciach są oparte na standardzie Unicode, wersja 8.0.0. )
Xanatos
0

Wypróbuj ten projekt simple-emoji-4j

Kompatybilny z Emoji 12.0 (2018.10.15)

Prosty z:

EmojiUtils.removeEmoji(str)
liheyuan
źródło
-2

Użyj wtyczki jQuery o nazwie RM-Emoji. Oto jak to działa:

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

Jest to tryb szybki, w którym niektóre emoji mogą nie być dostępne, ponieważ wykorzystuje algorytmy heurystyczne do wyszukiwania emoji w tekście. Użyj .full()metody, aby zeskanować cały ciąg i usunąć wszystkie emoji gwarantowane.

Atwood Mandelbrot-Spolsky
źródło
5
Pytanie było w Javie, dlatego wtyczka jQuery nie ma tu znaczenia.
riorio