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 ?


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.


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


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

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


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 😨😱😰😲😀.


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


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


5
Pytanie było w Javie, dlatego wtyczka jQuery nie ma tu znaczenia.
— riorio
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.