Удалите ✅, 🔥, ✈, ♛ и другие подобные эмодзи / изображения / знаки из строк 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.