Удалите ✅, 🔥, ✈, ♛ и другие подобные эмодзи / изображения / знаки из строк 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}), letter ( \\p{L}), mark ( \\p{M}), знаки пунктуации ( \\p{P}), пробелы / разделители ( \\p{Z}), другие форматирования ( \\p{Cf}) и другие символы, указанные выше U+FFFFв символах Unicode ( \\p{Cs}) и newline ( \\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 и смайликами - это акцентированные и нелатинские буквы. Без участия ОП мы не знаем, является ли это хорошим ответом (хотя не моим Д.В.)
— Крис Х

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

15
@ Крис большой пример регулярного выражения пунктуации, выглядит достаточно обширным для меня в некоторых случаях. Также, возможно, тогда люди не читают весь ответ - как указано в нижней части ответа, он 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 Спасибо за подробности! Я добавлю это в список.
— Даниэль Вагнер

29
Это ответ на будущее. Независимо от будущих обновлений стандарта Unicode, включение / исключение символов на основе их категорий означает, что отдельный анализ символов и ведение списка не требуется. Разумеется, следует провести поверхностное тестирование текста на разных языках (например, на китайском, арабском и т. Д.), Чтобы убедиться, что отфильтрованные категории соответствуют тексту, который требуется разрешить в целевой среде.
— CJBS

3
О, еще одна ошибка, о которой я должен был подумать вчера: TAB, CR и LF - это общая категория Cc (Java CONTROL). Они должны быть специально внесены в белый список, так как вы почти наверняка не хотите допускать большинство устаревших управляющих символов.
— Звол

@CJBS Проблема с этим подходом состоит в том, что он только частично реализован на Java. Например, вам Character.getType()не сообщат, является ли ваша char(или intкодовая точка, поскольку метод перегружен), скажем, смайликом, или музыкальным символом, или символом смайликов и т. Д. Если у вас есть простой вариант использования, это может быть хорошо идти по этому пути - это, безусловно, элегантный подход, который легко понять - но имейте в виду, что он может сломаться, если требования изменятся.
— Скомиса

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-septions.txt )
— xanatos,

49
По сути, это тот же подход, который используется EmojiParser, и вскоре он потерпит неудачу по той же причине. Новые смайлики относительно часто добавляются в базу данных символов Unicode, и если вы сейчас реализуете решение, использующее в настоящее время 1644 смайлика для набора отрицательных правил, реализация завершится неудачно, как только появятся новые смайлики.
— Jarnbjo

20

ICU4J твой друг.

UCharacter.hasBinaryProperty(UProperty.EMOJI);

Не забывайте обновлять свою версию icu4j и учтите, что это отфильтровывает только официальные символы Юникода, а не символьные символы. Объедините с фильтрацией других типов символов по желанию.

Дополнительная информация: 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
早上好 ♛
早上好 
Καλημέρα ✂
Καλημέρα 

Код работает путем потоковой передачи строки в кодовые точки. Затем с помощью лямбда-символов для фильтрации символов вint массив, мы конвертируем массив в строку.

В буквы и пробелы используются с использованием методов символов для фильтра, а не хорошие с пунктуацией. Неудачная попытка .

В Юникоде блоков белого фильтр с использованием Юникода блоков программист указует , как разрешено. Неудачная попытка .

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

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


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

Все ваши пути не прошли тесты.
— Олег

@ Олег, посмотри еще раз, 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.