Видаліть ✅, 🔥, ✈, ♛ та інші подібні емоджи / зображення / знаки зі струн Java


192

У мене є кілька рядків із різними емоджими / зображеннями / знаками в них.

Не всі рядки є англійською мовою - деякі з них є іншими не латинськими мовами, наприклад:

▓ 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 :

String withoutEmojis = EmojiParser.removeAllEmojis(input);

Проблема полягає в тому, що EmojiParser не в змозі усунути більшість ознак. Знак ♦ єдиний, який я до цього часу виявив, що його зняли. Інші ознаки, такі як ✪ ❉ ★ ✰ ❈ ❧ ✂ ❋ ⓡ ✿ ♛ 🔥, не видаляються.

Чи є можливість видалити всі ці знаки з вхідних рядків і зберегти лише літери та розділові знаки на різних мовах ?


91
що ти хочеш зберегти?
YCF_L

31
Дві проблеми: Що таке EmojiParser? Схоже, це не є частиною стандартної бібліотеки, тому ця згадка не дуже корисна. А які саме символи ви хочете фільтрувати? Ви кажете «ще багато подібного роду», але є багато символьних груп та сімей. Нам потрібно знати більше про ваші критерії.
Маркус Фішер

129
IDK, які ваші мотивації стоять за цим, але якщо це занадто фільтрує введення тексту: не робіть. Я втомився бути змушеним використовувати a-zA-Z. Дозвольте мені писати рідною мовою, або емоджи, або що завгодно. Я дійсно хочу, щоб призначення календаря називалося "🤦🏻‍♂️"? Так, так. Тепер піти з мого шляху.
Олександр - Відновіть Моніку

19
Поясніть, що саме ви хочете зберегти та видалити. З іншого боку, питання видається чітким, але через складність Unicode це не так, і тому неможливо дати хорошу відповідь.
Олег

12
це здається дивним, що хочеться зробити, коли це знищує сенс хоча б одного з ваших прикладів?
Eevee

Відповіді:


290

Замість створення чорних списків деяких елементів, як щодо створення списку символів, який ви хочете зберегти? Таким чином, вам не потрібно турбуватися про кожен доданий смайлик.

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}) та інші символи, зазначені вище U+FFFFу символах Unicode ( \\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 для вирівнювань.


4
Очевидний розрив між буквено-цифровими символами ASCII та смайлами є наголошеними та не латинськими літерами. Без вступу ОП у ці питання ми не знаємо, чи це хороша відповідь (не моя DV, хоча)
Кріс Х

4
Так, мені цікаво, чому це можливо може бути усунено. По-друге, я побачив це запитання, регулярний вираз був абсолютним першим, що мені прийшло в голову (PS, оскільки він шукає стандартних символів та пунктуації, я б використав щось на кшталт, [^\w\^\-\[\]\.!@#$%&*\(\)/+'":;~?,]але це просто я надійна і намагаюся зібрати всі типові персонажі, які не є т символи). Оновлено, оскільки це, безумовно, потенційне рішення. Якщо він хоче додати деякі інші символи мови, він може додати їх до виразу за необхідності.
Кріс

15
@Chris великий розділовий приклад пунктуації, здається мені досить обширним для деяких випадків. Також, можливо, люди тоді не читають всієї відповіді - як зазначено в нижній частині відповіді, p{L}обробляє неанглійські алфавітні символи . Я сподіваюсь, що зрозуміло, що я не можу широко перелічити всі неанглійські алфавіти у своїй відповіді, оскільки це було б непрактично багатослівним.
Нік Булл

12
Це. Будь ласка і дякую. Не намагайтеся забороняти символів, які створюють у вас проблеми; визначте, які символи ви дозволяєте та кодуйте. Тоді ваш код має чітко визначений набір тестових випадків.
jpmc26

2
Я пропоную "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\s]". Це дозволяє загальні категорії літера, позначка, цифра, пунктуація, роздільник та "інше, форматування", а також символи пробілів, такі як вкладка та новий рядок.
Шон Ван Гордер

81

Я не супер в 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, яку я не включав до списку вищевказаних категорій.)


1
FORMAT (Cf) також має бути збережений; Сюди входять перестановки кластеризації та спрямованості, без яких неможливо написати певні (незвичні, правда,) слова на деяких мовах.
zwol

@zwol Дякую за деталі! Я додам його до списку.
Даніель Вагнер

29
Це відповідь на майбутнє. Незалежно від майбутніх оновлень стандарту Unicode, включення / виключення символів на основі їх категорій означає, що індивідуальний аналіз символів та підтримка списку не потрібні. Зрозуміло, слід провести коротке тестування тексту різними мовами (наприклад, китайською, арабською тощо), щоб забезпечити відповідність відфільтрованих категорій тексту, необхідному для дозволу в цільовому середовищі.
CJBS

3
О, ще один прийом, про який я мав би подумати про вчора: TAB, CR та LF - загальна категорія Cc (управління Java). Вони повинні бути спеціально дозволеними, оскільки ви майже напевно не бажаєте дозволити більшість знаків, що передаються у спадщину.
zwol

@CJBS Проблема такого підходу полягає в тому, що він лише частково реалізований на Java. Наприклад, Character.getType()не повідомлять, чи ваш char(або intкодовий момент, оскільки метод перевантажений) є, скажімо, смайликом, або музичним символом, або символом емоджи тощо. Якщо у вас є простий випадок використання, можливо, це буде добре піти по цьому шляху - це, звичайно, елегантний підхід, який легко зрозуміти, - але пам’ятайте, що він може зламатися, якщо вимоги зміниться.
skomisa

47

На основі Повного списку Emoji, v11.0, ви маєте видалити 1644 різних кодів Unicode. Наприклад, є у цьому списку як U+2705.

Маючи повний список емоджи, вам потрібно відфільтрувати їх за допомогою кодів . Ітерація над одиночним charабо byteне працює, оскільки одна кодова точка може охоплювати кілька байтів. Оскільки 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);

28
Дуже корисний список. Цікаво, що щось, що називається EmojiParser методом, який називається removeAllEmojis, не справляється з цими ... :-)
TJ Crowder

7
@Bergi: Ні, оскільки input.codePointAtрозглядається лише до 2 символів, що є постійною верхньою межею. Також (нещодавно доданий) i += Character.charCount(cp)пропускає всі input.codePointAtперевірені символи (мінус 1 у деяких кутових випадках).
Девід Фоерстер

6
@ OlivierGrégoire: String.chars()потоки над символами, а не кодовими точками . Для цього існує окремий метод String.codePoints().
Девід Фоерстер

5
Тут є щонайменше дві проблеми: ви використовуєте "закритий" список емоджи, тому щороку вам доведеться розширювати його (але це, ймовірно, не легко вирішується), і цей код, ймовірно, не буде правильно працювати з послідовностями кодових точок (див., наприклад, unicode.org/Public/emoji/11.0/emoji-zwj-sequences.txt )
xanatos

49
Це в основному той самий підхід, який застосовував EmojiParser, і незабаром він провалиться з тієї ж причини. Нові смайли відносно часто додаються в базу даних символів Unicode, і якщо ви зараз реалізуєте рішення, використовуючи визначені на даний момент 1644 емоджи для негативного набору правил, реалізація не вдасться, як тільки нові емоджи стануть доступними.
jarnbjo

20

ICU4J - твій друг.

UCharacter.hasBinaryProperty(UProperty.EMOJI);

Не забудьте оновлювати свою версію icu4j та зверніть увагу, що це відфільтрує лише офіційні смайли Unicode, а не символи. Поєднайте з фільтруванням інших типів символів за бажанням.

Більше інформації: http://icu-project.org/apiref/icu4j/com/ibm/icu/lang/UProperty.html#EMOJI


1
Поки Java не буде оновлена, включаючи бінарне властивість Emoji, я думаю, це було б хорошим рішенням. Однак бібліотеку потрібно часто оновлювати для щойно доданих точок коду.
nhahtdh

10

Я наводив кілька прикладів нижче і вважав, що латиниці достатньо, але ...

Чи є можливість видалити всі ці знаки з вхідного рядка і зберегти лише літери та пунктуацію на різних мовах?

Після редагування розробив нове рішення, використовуючи 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
早上好 
早上好 
Καλημέρα 
Καλημέρα 

Код працює за допомогою потоку String до кодових точок. Потім використовуючи лямбдаси для фільтрації символів у intмасив, потім перетворюємо масив у String.

The букви і прогалини використовуються з використанням методів символів для фільтра, а не добрий з пунктуацією. Помилка спроби .

The Юникоде блоків білого фільтр з використанням Юнікоду блоків програміст указует , як дозволено. Помилка спроби .

The Юнікод блоки чорний фільтр з використанням Юнікоду блоків програміст визначає , як не допускається. Помилка спроби .

Категорія фільтра з допомогою статичного методу Character.getType. Програміст може визначити в categoryмасиві, які типи дозволені. РОБОТИ 😨😱😰😲😀.


import java.lang.Character.UnicodeBlock;, потім Character.UnicodeBlock-> UnicodeBlock.
Бернхард Баркер

Усі ваші шляхи провалили тести.
Олег

@Oleg ні, подивіться ще раз, white listприклад.
Маркос Зольновський

Щось має бути не так з моїми очима чи монітором, я не можу побачити це 早上 好 і Καλημέρα
Олег

4
Зауважте, що мова Java трохи повільно підтримує новіші версії Unicode ... Наприклад, Java 10 підтримує лише Unicode 8 (тому її класи символів описують лише символи Unicode 8) ... Так багато емоджи не представлені (див. Docs.oracle .com / javase / 10 / docs / api / java / lang / Character.html , інформація про персонаж базується на стандарті Unicode, версія 8.0.0. )
xanatos


-2

Використовуйте плагін jQuery під назвою RM-Emoji. Ось як це працює:

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

Це швидкий режим, який може пропустити деякі емоджи, оскільки він використовує евристичні алгоритми для пошуку емоджи в тексті. Використовуйте .full()метод для сканування всієї рядка та видалення всіх гарантованих емоцій.


5
Питання було в Java, тому плагін jQuery тут не доречний.
riorio
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.