Elimine ✅, 🔥, ✈, ♛ y otros emojis / imágenes / signos de las cadenas Java

192

Tengo algunas cadenas con todo tipo de diferentes emojis / imágenes / signos en ellas.

No todas las cadenas están en inglés; algunas están en otros idiomas no latinos, por ejemplo:

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

... y muchos más de estos.

Me gustaría deshacerme de todos estos signos / imágenes y mantener solo las letras (y la puntuación) en los diferentes idiomas.

Traté de limpiar los signos usando la biblioteca EmojiParser :

String withoutEmojis = EmojiParser.removeAllEmojis(input);

El problema es que EmojiParser no puede eliminar la mayoría de los signos. El signo ♦ es el único que encontré hasta ahora que se eliminó. Otros signos como ✪ ❉ ★ ✰ ❈ ❧ ✂ ❋ ⓡ ✿ ♛ 🔥 no se eliminan.

¿Hay alguna manera de eliminar todos estos signos de las cadenas de entrada y mantener solo las letras y la puntuación en los diferentes idiomas ?

riorio
fuente
9191
que quieres conservar
YCF_L
31
Dos problemas: ¿Qué es EmojiParser? No parece ser parte de una biblioteca estándar, por lo que esta mención no es muy útil. ¿Y qué caracteres quieres filtrar exactamente? Dices "muchos más de este tipo", pero hay muchos grupos de personajes y familias. Necesitamos saber más sobre sus criterios.
Markus Fischer
129129
IDK cuáles son tus motivaciones detrás de esto, pero si es demasiado filtro de entrada de texto: no lo hagas. Estoy cansado de ser obligado a usar a-zA-Z. Déjame escribir en mi idioma nativo, o emojis, o lo que quiera. ¿Realmente quiero que mi cita del calendario se llame "🤦🏻‍♂️"? Sí Sí lo hago. Ahora sal de mi camino.
Alexander - Restablece a Monica el
19
Por favor, aclare qué es exactamente lo que desea conservar y eliminar. En la superficie, la pregunta parece ser clara, pero debido a la complejidad de Unicode no lo es y por eso es imposible proporcionar una buena respuesta.
Oleg
12
¿Parece algo extraño querer hacer cuando destruye el significado de al menos uno de sus ejemplos?
Eevee

Respuestas:

290

En lugar de incluir en la lista negra algunos elementos, ¿qué le parece crear una lista blanca de los personajes que desea conservar? De esta manera, no necesita preocuparse por cada nuevo emoji que se agregue.

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

Entonces:

  • [\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]es un rango que representa todos los caracteres numéricos ( \\p{N}), letras ( \\p{L}), marcas ( \\p{M}), signos de puntuación ( \\p{P}), espacios en blanco / separadores ( \\p{Z}), otros formatos ( \\p{Cf}) y otros caracteres anteriores U+FFFFen Unicode ( \\p{Cs}) y caracteres de nueva línea ( \\s).\\p{L}Incluye específicamente los caracteres de otros alfabetos como cirílico, latín, kanji, etc.
  • los ^ conjunto de caracteres en la expresión regular niega la coincidencia.

Ejemplo:

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

Si necesita más información, consulte la documentación de Java para expresiones regulares.

Nick Bull
fuente
44
La brecha obvia entre los caracteres alfanuméricos ASCII y los emoji son las letras acentuadas y no latinas. Sin el aporte del OP sobre estos, no sabemos si esta es una buena respuesta (aunque no es mi DV)
Chris H
44
Sí, tengo curiosidad de por qué esto podría ser rechazado. En el segundo en que vi esta pregunta, una expresión regular fue lo primero que se me ocurrió (PD: ya que está buscando caracteres y signos de puntuación estándar, usaría algo como [^\w\^\-\[\]\.!@#$%&*\(\)/+'":;~?,]eso, pero solo soy robusto e intento reunir todos los caracteres típicos que no son 't símbolos). Votado porque definitivamente es una solución potencial. Si quiere agregar otros caracteres del lenguaje, puede agregarlos a la expresión según sea necesario.
Chris
15
@Chris gran ejemplo de expresión regular de puntuación, me parece bastante extenso para algunos casos. Además, tal vez las personas no estén leyendo la respuesta completa, como se indica en la parte inferior de la respuesta, p{L}maneja caracteres alfabéticos que no están en inglés . Espero que se entienda que no puedo enumerar extensamente a través de cada alfabeto que no esté en inglés en mi respuesta, ya que eso sería poco práctico.
Nick Bull
12
Esta. Por favor y gracias. No intentes prohibir personajes que te causen problemas; decide qué caracteres permites y codifica eso. Entonces su código tiene un conjunto claramente definido de casos de prueba.
jpmc26
2
Sugiero "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\s]". Esto permite las categorías generales Letra, Marca, Número, Puntuación, Separador y "Otro, Formato", así como los espacios en blanco como tabulación y nueva línea.
Sean Van Gorder
81

No estoy muy interesado en Java, por lo que no intentaré escribir código de ejemplo en línea, pero la forma en que lo haría es verificar lo que Unicode llama "la categoría general" de cada carácter. Hay un par de letras y categorías de puntuación.

Puede usar Character.getType para encontrar la categoría general de un personaje dado. Probablemente debería retener los caracteres que se encuentran en estas categorías generales:

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 los caracteres que enumeró como que desea eliminar específicamente tienen categoría general OTHER_SYMBOL, que no incluí en la lista blanca de la categoría anterior).

Daniel Wagner
fuente
1
FORMATO (Cf) debe conservarse también; Esto incluye el agrupamiento y las anulaciones direccionales, sin las cuales es imposible escribir ciertas palabras (inusuales, ciertamente) en algunos idiomas.
zwol
@zwol Gracias por los detalles! Lo agregaré a la lista.
Daniel Wagner
29
Esta es la respuesta a prueba de futuro. Independientemente de las futuras actualizaciones del estándar Unicode, incluir / excluir caracteres basados ​​en sus categorías significa que el análisis individual de caracteres y el mantenimiento de una lista es innecesario. Por supuesto, se deben realizar pruebas rápidas de texto en diferentes idiomas (p. Ej., Chino, árabe, etc.) para garantizar que las categorías filtradas coincidan con el texto que debe permitirse en el entorno de destino.
CJBS
3
Oh, otro problema que debería haber pensado ayer: TAB, CR y LF son todos de categoría general Cc (CONTROL de Java). Es necesario que estén especialmente en la lista blanca, ya que casi seguro que no desea permitir la mayoría de los caracteres de control heredados.
zwol
@CJBS El problema con este enfoque es que solo se ha implementado parcialmente en Java. Por ejemplo, Character.getType()no le dirá si su char(o intpunto de código ya que el método está sobrecargado) es, por ejemplo, un emoticón o un símbolo musical, o un personaje emoji, etc. Si tiene un caso de uso simple, podría estar bien seguir este camino, sin duda es un enfoque elegante que es fácil de comprender, pero tenga en cuenta que podría romperse si cambian los requisitos.
skomisa
47

Basado en la lista completa de Emoji, v11.0 tiene 1644 puntos de código Unicode diferentes para eliminar. Por ejemplo está en esta lista como U+2705.

Tener la lista completa de emojis necesita filtrarlos usando puntos de código . Iterar sobre solo charo byteno funcionará como punto de código único puede abarcar múltiples bytes. Debido a que Java usa emojis UTF-16, generalmente tomará dos charsegundos.

String input = "ab✅cd";
for (int i = 0; i < input.length();) {
  int cp = input.codePointAt(i);
  // filter out if matches
  i += Character.charCount(cp); 
}

El mapeo desde el punto de código Unicode U+2705a Java intes sencillo:

int viSign = 0x2705;

o como Java admite cadenas Unicode:

int viSign = "✅".codePointAt(0);
Karol Dowbecki
fuente
28
Lista muy útil. Es interesante que algo llamado EmojiParser con un método llamado removeAllEmojis no pueda manejar estos ... :-)
TJ Crowder
77
@Bergi: No, ya que input.codePointAtsolo mira hasta 2 caracteres como máximo, lo que es un límite superior constante. Además (el recién agregado) i += Character.charCount(cp)omite todos los caracteres que input.codePointAtinspeccionaron (menos 1 en algunos casos de esquina).
David Foerster
66
@ OlivierGrégoire: String.chars()transmite sobre caracteres no puntos de código. Hay un método separado String.codePoints()para eso.
David Foerster
55
Aquí hay al menos dos problemas: está utilizando una lista "cerrada" de emojis, por lo que cada año tiene que extenderla (pero esto probablemente no sea fácil de resolver), y este código probablemente no funcionará correctamente con secuencias de puntos de código (ver por ejemplo unicode.org/Public/emoji/11.0/emoji-zwj-sequences.txt )
xanatos
49
Este es básicamente el mismo enfoque utilizado por EmojiParser y pronto fallará por la misma razón. Los nuevos emojis se agregan con relativa frecuencia a la base de datos de caracteres Unicode y si ahora está implementando una solución utilizando los emojis 1644 definidos actualmente para un conjunto de reglas negativas, la implementación fallará tan pronto como estén disponibles nuevos emojis.
jarnbjo
20

ICU4J es tu amigo.

UCharacter.hasBinaryProperty(UProperty.EMOJI);

Recuerde mantener su versión de icu4j actualizada y tenga en cuenta que esto solo filtrará los emoji Unicode oficiales, no los caracteres de símbolos. Combine con el filtrado de otros tipos de caracteres como desee.

Más información: http://icu-project.org/apiref/icu4j/com/ibm/icu/lang/UProperty.html#EMOJI

Daniel F
fuente
1
Hasta que Java se actualice para incluir la propiedad binaria Emoji, supongo que esta sería una buena solución. Sin embargo, la biblioteca debe actualizarse a menudo para los puntos de código recién agregados.
nhahtdh
10

Di algunos ejemplos a continuación, y pensé que el latín es suficiente, pero ...

¿Hay alguna manera de eliminar todos estos signos de la cadena de entrada y mantener solo las letras y la puntuación en los diferentes idiomas?

Después de editar, desarrolló una nueva solución, utilizando el Character.getTypemétodo, y esa parece ser la mejor opción para esto.

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

}

Salida:

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

El código funciona transmitiendo la cadena a los puntos de código. Luego, usando lambdas para filtrar caracteres en unint matriz, luego convertimos la matriz a Cadena.

Las letras y los espacios se utilizan utilizando los métodos de Carácter para filtrar, no son buenos con la puntuación. Intento fallido .

El Unicode bloquea el filtro blanco utilizando los bloques Unicode que el programador especifica como permitidos. Intento fallido .

El Unicode bloquea el filtro negro utilizando los bloques Unicode que el programador especifica como no permitidos. Intento fallido .

El filtro de categoría con el método estático Character.getType. El programador puede definir en la categorymatriz qué tipos están permitidos. FUNCIONA 😨😱😰😲😀.

Marcos Zolnowski
fuente
import java.lang.Character.UnicodeBlock;, entonces Character.UnicodeBlock-> UnicodeBlock.
Bernhard Barker
Todos tus caminos fallaron las pruebas.
Oleg
@Oleg no, mira de nuevo, el white listejemplo.
Marcos Zolnowski
Algo debe estar mal con mis ojos o mi monitor, no puedo ver es 早上 好 y Καλημέρα
Oleg
44
Tenga en cuenta que el lenguaje Java es un poco lento y admite versiones Unicode más nuevas ... Por ejemplo, Java 10 solo admite Unicode 8 (por lo que sus clases de caracteres solo describen caracteres Unicode 8) ... Muchos emojis no están presentes (ver docs.oracle .com / javase / 10 / docs / api / java / lang / Character.html , la información del personaje se basa en el estándar Unicode, versión 8.0.0. )
xanatos
0

Prueba este proyecto simple-emoji-4j

Compatible con Emoji 12.0 (2018.10.15)

Simple con:

EmojiUtils.removeEmoji(str)
liheyuan
fuente
-1

Use un complemento jQuery llamado RM-Emoji. Así es como funciona:

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

Este es el modo rápido que puede perder algunos emojis, ya que utiliza algoritmos heurísticos para encontrar emojis en el texto. Use el .full()método para escanear toda la cadena y eliminar todos los emojis garantizados.

Atwood Mandelbrot-Spolsky
fuente
55
La pregunta estaba en Java, por lo tanto, un complemento jQuery no es relevante aquí.
riorio