Remova ✅, 🔥, ✈, ♛ e outros emojis / imagens / sinais de strings Java

192

Eu tenho algumas cordas com todos os tipos de emojis / imagens / sinais diferentes.

Nem todas as strings estão em inglês - algumas delas estão em outros idiomas não latinos, por exemplo:

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

... e muito mais.

Gostaria de me livrar de todos esses sinais / imagens e manter apenas as letras (e pontuação) nos diferentes idiomas.

Tentei limpar os sinais usando a biblioteca EmojiParser :

String withoutEmojis = EmojiParser.removeAllEmojis(input);

O problema é que o EmojiParser não consegue remover a maioria dos sinais. O sinal ♦ é o único que encontrei até agora que foi removido. Outros sinais como ✪ ❉ ★ ✰ ❈ ❧ ❋ ⓡ ✿ ♛ 🔥 não são removidos.

Existe uma maneira de remover todos esses sinais das seqüências de caracteres de entrada e mantendo apenas as letras e a pontuação nos diferentes idiomas ?

riorio
fonte
91
o que você quer manter?
YCF_L 27/03
31
Dois problemas: O que é o EmojiParser? Não parece fazer parte de uma biblioteca padrão, portanto, essa menção não é muito útil. E quais caracteres exatamente você deseja filtrar? Você diz "muito mais desse tipo", mas há muitos grupos de personagens e famílias. Precisamos saber mais sobre seus critérios.
Markus Fischer
129
Identifique quais são suas motivações por trás disso, mas se for muito filtro de entrada de texto: não. Estou cansado de ser forçado a usar a-zA-Z. Deixe-me escrever na minha língua nativa, ou emojis, ou o que eu quiser. Eu realmente quero que eu marque um horário para ser chamado de "🤦🏻‍♂️"? Sim Sim eu faço. Agora saia do meu caminho.
Alexander - Restabelece Monica
19
Esclareça o que exatamente você deseja manter e remover. Aparentemente, a questão parece clara, mas devido à complexidade do Unicode, não é e, por isso, é impossível fornecer uma boa resposta.
Oleg
12
isso parece uma coisa estranha a se fazer quando destrói o significado de pelo menos um de seus exemplos?
Eevee

Respostas:

290

Em vez de colocar alguns elementos na lista negra, que tal criar uma lista branca dos personagens que você deseja manter? Dessa forma, você não precisa se preocupar com a adição de novos emojis.

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

Assim:

  • [\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]é um intervalo que representa todos os caracteres numéricos ( \\p{N}), letra ( \\p{L}), marca ( \\p{M}), pontuação ( \\p{P}), espaço em branco / separador ( \\p{Z}), outra formatação ( \\p{Cf}) e outros caracteres acima U+FFFFnos caracteres Unicode ( \\p{Cs}) e nova linha ( \\s). inclui \\p{L}especificamente os caracteres de outros alfabetos, como cirílico, latino, kanji etc.
  • O ^conjunto de caracteres regex nega a correspondência.

Exemplo:

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

Se precisar de mais informações, consulte a documentação do Java para regexes.

Nick Bull
fonte
4
A diferença óbvia entre caracteres alfanuméricos ASCII e emoji é letras acentuadas e não latinas. Sem entrada do OP sobre estes não sabemos se esta é uma boa resposta (não meu DV embora)
Chris H
4
Sim, eu estou curioso para saber por que isso poderia ser rebaixado. No segundo em que vi essa pergunta, uma expressão regular foi a primeira coisa que me veio à mente (PS, já que ele está procurando caracteres e pontuação padrão, eu usaria algo como, [^\w\^\-\[\]\.!@#$%&*\(\)/+'":;~?,]mas sou apenas eu sendo robusta e tentando coletar todos os personagens típicos que não são símbolos). Promovido porque esta é definitivamente uma solução potencial. Se ele quiser adicionar outros caracteres de idioma, ele poderá adicioná-los à expressão conforme necessário.
27418 Chris
15
O exemplo de regex de pontuação grande do Chris, parece extenso o suficiente para mim em alguns casos. Além disso, talvez as pessoas não estejam lendo a resposta inteira, como indicado na parte inferior da resposta, p{L}lida com caracteres alfabéticos não ingleses . Espero que entenda que não consigo listar extensivamente todos os alfabeto que não sejam o inglês na minha resposta, pois isso seria impraticável.
27518 Nick Bull
12
Este. Por favor e obrigado. Não tente proibir caracteres que causam problemas; decida quais caracteres você permitirá e codifique isso. Então, seu código tem um conjunto claramente definido de casos de teste.
jpmc26
2
Eu sugiro "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\s]". Isso permite as categorias gerais Carta, Marca, Número, Pontuação, Separador e "Outros, Formato", além de caracteres de espaço em branco, como tabulação e nova linha.
Sean Van Gorder 28/03
81

Eu não sou muito apaixonado por Java, então não tentarei escrever um exemplo de código embutido, mas a maneira como faria isso é verificar o que o Unicode chama de "a categoria geral" de cada caractere. Existem algumas categorias de letras e pontuação.

Você pode usar Character.getType para encontrar a categoria geral de um determinado caractere. Você provavelmente deve reter os caracteres que se enquadram nestas categorias gerais:

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

(Todos os caracteres que você listou como especificamente querendo remover têm categoria geral OTHER_SYMBOL, que eu não incluí na lista de permissões da categoria acima.)

Daniel Wagner
fonte
1
FORMAT (Cf) também deve ser preservado; isso inclui agrupamentos e substituições direcionais, sem as quais é impossível escrever certas palavras (incomuns, é verdade) em alguns idiomas.
Zwol 27/03
@ zwol Obrigado pelos detalhes! Vou adicioná-lo à lista.
Daniel Wagner
29
Esta é a resposta pronta para o futuro. Independentemente de futuras atualizações do padrão Unicode, incluir / excluir caracteres com base em suas categorias significa que a análise individual de caracteres e a manutenção de uma lista são desnecessárias. Obviamente, testes rápidos de texto em diferentes idiomas (por exemplo, chinês, árabe etc.) devem ser feitos para garantir que as categorias filtradas correspondam ao texto que é necessário ser permitido no ambiente de destino.
CJBS
3
Ah, outro problema que eu deveria ter pensado ontem: TAB, CR e LF são todos da categoria geral Cc (Java's CONTROL). Eles precisam ser especialmente incluídos na lista de permissões, pois você quase certamente não deseja permitir a maioria dos caracteres de controle herdados.
Zwol
@CJBS O problema dessa abordagem é que ela foi implementada apenas parcialmente em Java. Por exemplo, Character.getType()não informará se seu char(ou intponto de código, pois o método está sobrecarregado) é, digamos, um emoticon ou um símbolo musical ou um caractere emoji, etc. Se você tiver um caso de uso simples, pode ser bom seguir esse caminho - certamente é uma abordagem elegante e fácil de entender -, mas lembre-se de que pode quebrar se os requisitos mudarem.
Skomisa
47

Com base na Lista completa de Emojis, v11.0, você tem 1644 pontos de código Unicode diferentes para remover. Por exemplo, está nesta lista como U+2705.

Tendo a lista completa de emojis, você precisa filtrá-los usando pontos de código . A iteração sobre um único charou bytenão funcionará, pois um ponto de código único pode abranger vários bytes. Como o Java usa emojis UTF-16, normalmente leva dois 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); 
}

O mapeamento do ponto de código Unicode U+2705para Java inté direto:

int viSign = 0x2705;

ou como o Java suporta Unicode Strings:

int viSign = "✅".codePointAt(0);
Karol Dowbecki
fonte
28
Lista muito útil. Interessante que algo chamado EmojiParser com um método chamado removeAllEmojis não consegue lidar com estes ... :-)
TJ Crowder
7
@ Bergi: Não, já que input.codePointAtapenas olha para até 2 caracteres, o que é um limite superior constante. Também (o recém-adicionado) i += Character.charCount(cp)pula todos os caracteres que input.codePointAtinspecionaram (menos 1 em alguns casos de canto).
David Foerster
6
@ OlivierGrégoire: String.chars()transmite caracteres, não pontos de código. Existe um método separado String.codePoints()para isso.
David Foerster
5
Há pelo menos dois problemas aqui: você está usando uma lista "fechada" de emojis; portanto, a cada ano você deve estendê-la (mas isso provavelmente não é facilmente solucionável) e esse código provavelmente não funcionará corretamente com sequências de pontos de código (veja por exemplo unicode.org/Public/emoji/11.0/emoji-zwj-sequences.txt )
xanatos 27/03
49
Essa é basicamente a mesma abordagem usada pelo EmojiParser e logo falhará pelo mesmo motivo. Novos emojis são adicionados com relativa frequência ao banco de dados de caracteres Unicode e se você estiver implementando uma solução usando os emojis 1644 atualmente definidos para um conjunto de regras negativo, a implementação falhará assim que novos emojis estiverem disponíveis.
jarnbjo
20

ICU4J é seu amigo.

UCharacter.hasBinaryProperty(UProperty.EMOJI);

Lembre-se de manter sua versão do icu4j atualizada e observe que isso filtrará apenas emojis Unicode oficiais, não caracteres simbólicos. Combine com a filtragem de outros tipos de caracteres, conforme desejado.

Mais informações: http://icu-project.org/apiref/icu4j/com/ibm/icu/lang/UProperty.html#EMOJI

Daniel F
fonte
1
Até que o Java seja atualizado para incluir a propriedade binária Emoji, acho que essa seria uma boa solução. A biblioteca precisa ser atualizada frequentemente para os novos pontos de código adicionados.
N
10

Dei alguns exemplos abaixo e pensei que o latim é suficiente, mas ...

Existe uma maneira de remover todos esses sinais da string de entrada e manter apenas as letras e a pontuação nos diferentes idiomas?

Após a edição, desenvolveu uma nova solução, usando o Character.getTypemétodo, e essa parece ser a melhor opção.

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

}

Resultado:

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

O código funciona transmitindo a String para pontos de código. Em seguida, use lambdas para filtrar caracteres em umint matriz, convertemos a matriz em String.

As letras e os espaços estão usando o método Character para filtrar, não é bom com pontuação. Falha na tentativa .

O unicode bloqueia o filtro branco usando os blocos unicode que o programador especifica conforme permitido. Falha na tentativa .

O unicode bloqueia o filtro preto usando os blocos unicode que o programador especifica como não permitido. Falha na tentativa .

O filtro de categoria usando o método estático Character.getType. O programador pode definir na categorymatriz que tipos são permitidos. TRABALHOS 😨😱😰😲😀.

Marcos Zolnowski
fonte
import java.lang.Character.UnicodeBlock;, então Character.UnicodeBlock-> UnicodeBlock.
Bernhard Barker
Todos os seus caminhos falharam nos testes.
Oleg
@ Oleg não, olhe novamente, o white listexemplo.
Marcos Zolnowski 27/03
Algo deve estar errado com meus olhos ou meu monitor, eu não posso ver é早上好e Καλημέρα
Oleg
4
Observe que a linguagem Java é um pouco lenta, suportando versões Unicode mais recentes ... Por exemplo, o Java 10 suporta apenas Unicode 8 (portanto, suas classes de caracteres descrevem apenas caracteres Unicode 8) ... Muitos emojis não estão presentes (consulte docs.oracle .com / javase / 10 / docs / api / java / lang / Character.html , As informações dos caracteres são baseadas no padrão Unicode, versão 8.0.0. )
xanatos
0

Experimente este projeto simple-emoji-4j

Compatível com Emoji 12.0 (2018.10.15)

Simples com:

EmojiUtils.removeEmoji(str)
liheyuan
fonte
-1

Use um plugin jQuery chamado RM-Emoji. Veja como funciona:

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

Esse é o modo rápido que pode perder alguns emojis, pois usa algoritmos heurísticos para encontrar emojis no texto. Use o .full()método para verificar a seqüência inteira e remover todos os emojis garantidos.

Atwood Mandelbrot-Spolsky
fonte
5
A questão estava em Java, portanto, um plugin jQuery não é relevante aqui.
Riorio 14/04