Conversión de símbolos, letras de acento al alfabeto inglés


129

El problema es que, como saben, hay miles de caracteres en el gráfico Unicode y quiero convertir todos los caracteres similares a las letras que están en alfabeto inglés.

Por ejemplo, aquí hay algunas conversiones:

ҥ->H
Ѷ->V
Ȳ->Y
Ǭ->O
Ƈ->C
tђє Ŧค๓เℓy --> the Family
...

y vi que hay más de 20 versiones de la letra A / a. y no sé cómo clasificarlos. Parecen agujas en el pajar.

La lista completa de caracteres unicode se encuentra en http://www.ssec.wisc.edu/~tomw/java/unicode.html o http://unicode.org/charts/charindex.html . Solo intenta desplazarte hacia abajo y ver las variaciones de las letras.

¿Cómo puedo convertir todo esto con Java? Por favor, ayúdame :(


Consulte esta pregunta: stackoverflow.com/questions/249087/… : también debería haber otras preguntas sobre este tema, pero no puedo encontrarlas en este momento.
schnaader

1
¿Debería su tercer ejemplo ser Ȳ → Y?
Dour High Arch

2
¿Por qué quieres hacer esto? Si supiéramos cuál es su objetivo general, podríamos ser más útiles.
David Thornley

David, sabes que algunos EMO usan diferentes caracteres en las oraciones. Aquí tienes un ejemplo: ฬ. ¢. tђє ฬ ย η∂єг ¢ ค ק ђ Ŧ ค ๓ เ ℓy <- Resuelve esto :) @schnaader, creo que eso es lo que estoy buscando pero no en Java.
AhmetB - Google

Esta conversación se ha hecho antes; vea @schnaader arriba.
dkretz

Respuestas:


197

Volver a publicar mi publicación de ¿Cómo elimino los signos diacríticos (acentos) de una cadena en .NET?

Este método funciona bien en Java (con el único fin de eliminar los signos diacríticos, también conocidos como acentos) .

Básicamente, convierte todos los caracteres acentuados en sus contrapartes desacentuados, seguidos de su combinación de signos diacríticos. Ahora puede usar una expresión regular para quitar los signos diacríticos.

import java.text.Normalizer;
import java.util.regex.Pattern;

public String deAccent(String str) {
    String nfdNormalizedString = Normalizer.normalize(str, Normalizer.Form.NFD); 
    Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
    return pattern.matcher(nfdNormalizedString).replaceAll("");
}

44
InCombiningDiacriticalMarks no convierte todos los cirílicos. Por ejemplo, Општина Богомила no ha sido tocado. Sería bueno si se pudiera convertirlo en opstina Bogomila o algo
Yvain

13
No translitera en absoluto. Simplemente elimina las marcas diacríticas descompuestas ("acentos"). El paso anterior (Form.NFD) desglosa á en un + ', es decir, descompone el carácter acentuado en un carácter sin acento más una marca diacrítica. Esto convertiría cirílico Ѽ en ​​Ѡ pero no más.
MSalters

1
George publicó que podría ser mejor usar \\ p {IsM} en lugar de \\ p {InCombiningDiacriticalMarks} en glaforge.appspot.com/article/… Tenga en cuenta que no lo he probado.
ATorras

2
\\ p {IsM} no parece funcionar para acentos españoles como á ó ú ñ é í. Por el contrario, "\\ p {InCombiningDiacriticalMarks} + está funcionando bien para esto
Loic

No funciona para todos los caracteres especiales: envié un problema incorrecto para Android para que aprenda eso -> code.google.com/p/android/issues/detail?id=189515 ¿Alguien sabe la forma correcta de hacer esto?
Michał Tajchert

71

Es una parte de Apache Commons Lang a partir de la versión. 3.0.

org.apache.commons.lang3.StringUtils.stripAccents("Añ");

devoluciones An

Ver también http://www.drillio.com/en/software-development/java/removing-accents-diacritics-in-any-language/


Esta solución es asombrosa. ¡Funciona también con griego! Gracias.
Tom

55
No es perfecto para la traducción de caracteres polacos de ł y Ł falta: entrada: ŚŻÓŁĄĆĘŹąółęąćńŃ salida: SZOŁACEZaołeacnN
Robert

1
Buena utilidad, pero dado que su código es exactamente el mismo que el que se muestra en la respuesta aceptada, y no desea agregar una dependencia de Commons Lang, puede usar el fragmento mencionado anteriormente.
polaretto

1
con apache común en mi caso: Đ no convertir a D
Hoang

@Hoang, Robert tal vez la oportunidad de enviar una solicitud de extracción :)
Ondra Žižka

19

Intentar "convertirlos a todos" es el enfoque equivocado del problema.

En primer lugar, debe comprender las limitaciones de lo que está tratando de hacer. Como otros han señalado, los signos diacríticos están ahí por una razón: son letras esencialmente únicas en el alfabeto de ese idioma con su propio significado / sonido, etc .: eliminar esas marcas es lo mismo que reemplazar letras al azar en una palabra en inglés. Esto es incluso antes de considerar los idiomas cirílicos y otros textos basados ​​en guiones como el árabe, que simplemente no se pueden "convertir" al inglés.

Si debes , por cualquier razón, convertir caracteres, entonces la única forma sensata de abordarlo es reducir primero el alcance de la tarea en cuestión. Considere la fuente de la entrada: si está codificando una aplicación para "el mundo occidental" (para usar una frase tan buena como cualquiera), sería poco probable que alguna vez necesite analizar los caracteres árabes. Del mismo modo, el conjunto de caracteres Unicode contiene cientos de símbolos matemáticos y gráficos: no hay una forma (fácil) para que los usuarios ingresen directamente estos, por lo que puede suponer que pueden ignorarse.

Al seguir estos pasos lógicos, puede reducir la cantidad de caracteres posibles para analizar hasta el punto en que sea factible una operación de búsqueda / reemplazo basada en el diccionario. Luego se convierte en una pequeña cantidad de trabajo un poco aburrido crear los diccionarios, y una tarea trivial para realizar el reemplazo. Si su idioma admite caracteres Unicode nativos (como lo hace Java) y optimiza las estructuras estáticas correctamente, tales hallazgos y reemplazos tienden a ser cegadoramente rápidos.

Esto proviene de la experiencia de haber trabajado en una aplicación que se requería para permitir a los usuarios finales buscar datos bibliográficos que incluían caracteres diacríticos. Las matrices de búsqueda (como en nuestro caso) tardaron quizás 1 día hombre en producirse, para cubrir todas las marcas diacríticas para todos los idiomas de Europa occidental.


Gracias por responder. En realidad no estoy trabajando con idiomas árabes o algo así. Sabes que algunas personas usan los signos diacríticos como personajes divertidos y tengo que eliminar eso tanto como puedo. Por ejemplo, en el ejemplo dije "tђє Ŧ ค ๓ เ ℓy -> la familia", pero parece difícil convertirlo por completo. Sin embargo, podemos hacer la conversión "òéışöç-> oeisoc" de una manera simple. Pero, ¿cuál es la forma exacta de hacer esto? ¿Creando matrices y reemplazando manualmente? ¿O este lenguaje tiene funciones nativas sobre este tema?
AhmetB - Google

15

Dado que la codificación que convierte "la Familia" en "tђє Ŧ ค ๓ เ ℓy" es efectivamente aleatoria y no sigue ningún algoritmo que pueda explicarse por la información de los puntos de código Unicode involucrados, no hay una forma general de resolver esto algorítmicamente.

Deberá construir la asignación de caracteres Unicode en caracteres latinos a los que se parecen. Probablemente podría hacer esto con algo de aprendizaje automático inteligente sobre los glifos reales que representan los puntos de código Unicode. Pero creo que el esfuerzo para esto sería mayor que construir manualmente ese mapeo. Especialmente si tiene una buena cantidad de ejemplos a partir de los cuales puede construir su mapeo.

Para aclarar: algunas de las sustituciones pueden resolverse realmente a través de los datos Unicode (como lo demuestran las otras respuestas), pero algunas letras simplemente no tienen una asociación razonable con los caracteres latinos a los que se parecen.

Ejemplos:

  • "ђ" (U + 0452 CYRILLIC SMALL LETTER DJE) está más relacionado con "d" que con "h", pero se utiliza para representar "h".
  • "Ŧ" (U + 0166 LETRA T CAPITAL LATINA CON CARRERA) está algo relacionado con "T" (como su nombre lo indica) pero se usa para representar "F".
  • "ค" (U + 0E04 THAI CHARACTER KHO KHWAI) no está relacionado con ningún carácter latino y en su ejemplo se usa para representar "a"

7

La solicitud original ya ha sido respondida.

Sin embargo, estoy publicando la respuesta a continuación para aquellos que podrían estar buscando un código genérico de transliteración para transliterar cualquier juego de caracteres al latín / inglés en Java.

Significado ingenuo de la transliteración: la cadena traducida en su forma final / juego de caracteres objetivo suena como la cadena en su forma original. Si queremos transcribir cualquier juego de caracteres al latín (alfabetos en inglés), entonces ICU4 (biblioteca ICU4J en Java) hará el trabajo.

Aquí está el fragmento de código en Java:

    import com.ibm.icu.text.Transliterator; //ICU4J library import

    public static String TRANSLITERATE_ID = "NFD; Any-Latin; NFC";
    public static String NORMALIZE_ID = "NFD; [:Nonspacing Mark:] Remove; NFC";

    /**
    * Returns the transliterated string to convert any charset to latin.
    */
    public static String transliterate(String input) {
        Transliterator transliterator = Transliterator.getInstance(TRANSLITERATE_ID + "; " + NORMALIZE_ID);
        String result = transliterator.transliterate(input);
        return result;
    }

7

Cadena probada: ÁÂÄÄÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝß

Probado:

  • Salida de Apache Commons Lang3 : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUYß
  • Salida de ICU4j : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUYß
  • Salida de JUnidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUUss (problema con Ý y otro problema )
  • Salida de Unidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUYss

La última opción es la mejor.


1
@mehmet Simplemente siga el archivo Léame en github.com/xuender/unidecode . Debe ser algo así como Unidecode.decode ("ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝß") después de importar la dependencia.
cactuschibre

6

Si la necesidad es convertir "òéışöç-> oeisoc", puede usar este un punto de partida:

public class AsciiUtils {
    private static final String PLAIN_ASCII =
      "AaEeIiOoUu"    // grave
    + "AaEeIiOoUuYy"  // acute
    + "AaEeIiOoUuYy"  // circumflex
    + "AaOoNn"        // tilde
    + "AaEeIiOoUuYy"  // umlaut
    + "Aa"            // ring
    + "Cc"            // cedilla
    + "OoUu"          // double acute
    ;

    private static final String UNICODE =
     "\u00C0\u00E0\u00C8\u00E8\u00CC\u00EC\u00D2\u00F2\u00D9\u00F9"             
    + "\u00C1\u00E1\u00C9\u00E9\u00CD\u00ED\u00D3\u00F3\u00DA\u00FA\u00DD\u00FD" 
    + "\u00C2\u00E2\u00CA\u00EA\u00CE\u00EE\u00D4\u00F4\u00DB\u00FB\u0176\u0177" 
    + "\u00C3\u00E3\u00D5\u00F5\u00D1\u00F1"
    + "\u00C4\u00E4\u00CB\u00EB\u00CF\u00EF\u00D6\u00F6\u00DC\u00FC\u0178\u00FF" 
    + "\u00C5\u00E5"                                                             
    + "\u00C7\u00E7" 
    + "\u0150\u0151\u0170\u0171" 
    ;

    // private constructor, can't be instanciated!
    private AsciiUtils() { }

    // remove accentued from a string and replace with ascii equivalent
    public static String convertNonAscii(String s) {
       if (s == null) return null;
       StringBuilder sb = new StringBuilder();
       int n = s.length();
       for (int i = 0; i < n; i++) {
          char c = s.charAt(i);
          int pos = UNICODE.indexOf(c);
          if (pos > -1){
              sb.append(PLAIN_ASCII.charAt(pos));
          }
          else {
              sb.append(c);
          }
       }
       return sb.toString();
    }

    public static void main(String args[]) {
       String s = 
         "The result : È,É,Ê,Ë,Û,Ù,Ï,Î,À,Â,Ô,è,é,ê,ë,û,ù,ï,î,à,â,ô,ç";
       System.out.println(AsciiUtils.convertNonAscii(s));
       // output : 
       // The result : E,E,E,E,U,U,I,I,A,A,O,e,e,e,e,u,u,i,i,a,a,o,c
    }
}

JDK 1.6 proporciona la clase java.text.Normalizer que se puede usar para esta tarea.

Mira un ejemplo aquí


Desafortunadamente eso no manejará ligaduras como Æ.
Dour High Arch

Este método es particularmente útil si necesita detectar y manejar clases de diacríticos de manera diferente (es decir, escapar caracteres especiales en LaTeX).
vallismortis 01 de

4

Puede intentar usar unidecode, que está disponible como una gema de rubí y como un módulo perl en cpan . Esencialmente, funciona como una gran tabla de búsqueda, donde cada punto de código Unicode se relaciona con un carácter o cadena ascii.


Es posible que pueda obtener una tabla de búsqueda de uno de estos.
Kathy Van Stone

Este es un paquete increíble, pero translitera el sonido del personaje, por ejemplo, convierte "北" a "Bei" porque así es como suena el personaje en mandarín. Creo que el interlocutor quiere convertir los glifos a lo que visualmente se parecen en inglés.
Dour High Arch

Sin embargo, hace eso para los caracteres latinos. â se convierte en a, et al. @ahmetalpbalkan Estoy de acuerdo con Kathy, podría usarlo como un recurso para construir su propia tabla de búsqueda, la lógica debería ser bastante simple. Desafortunadamente, no parece haber una versión de Java.
Daniel Vandersluis

@ahmetalpbalkan Aquí hay unidecode para Java.
Jakub Jirutka

4

No hay una manera fácil o general de hacer lo que quieres porque es solo tu opinión subjetiva de que estas letras se parecen a las letras latinas a las que deseas convertir. En realidad, son letras separadas con sus propios nombres y sonidos distintos que simplemente parecen superficialmente una letra latina.

Si desea esa conversión, debe crear su propia tabla de traducción basada en las letras latinas a las que cree que deberían convertirse las letras no latinas.

(Si solo desea eliminar las marcas diacríticas, hay algunas respuestas en este hilo: ¿Cómo elimino los signos diacríticos (acentos) de una cadena en .NET? Sin embargo, describe un problema más general)


+1. Aquí hay una versión Java de la pregunta 'eliminar diacríticos': stackoverflow.com/questions/1016955/… ; ver las respuestas de Michael Borgwardt y devio
Jonik

4

Llego tarde a la fiesta, pero después de enfrentar este problema hoy, encontré que esta respuesta es muy buena:

String asciiName = Normalizer.normalize(unicodeName, Normalizer.Form.NFD)
    .replaceAll("[^\\p{ASCII}]", "");

Referencia: https://stackoverflow.com/a/16283863


Pequeña advertencia: elimina U + 00DF LETRA LETRA PEQUEÑA S "ß"
rafalmag

Y también Æ ... Al mal.
cactuschibre

4

El problema con la "conversión" de Unicode arbitrario a ASCII es que el significado de un personaje depende de la cultura. Por ejemplo, "ß" para una persona de habla alemana debería convertirse a "ss", mientras que un angloparlante probablemente lo convertiría a "B".

Agregue a eso el hecho de que Unicode tiene múltiples puntos de código para los mismos glifos.

El resultado es que la única forma de hacerlo es crear una tabla masiva con cada carácter Unicode y el carácter ASCII al que desea convertirlo. Puede tomar un atajo normalizando caracteres con acentos para normalizar desde KD, pero no todos los caracteres se normalizan a ASCII. Además, Unicode no define qué partes de un glifo son "acentos".

Aquí hay un pequeño extracto de una aplicación que hace esto:

switch (c)
{
    case 'A':
    case '\u00C0':  //  À LATIN CAPITAL LETTER A WITH GRAVE
    case '\u00C1':  //  Á LATIN CAPITAL LETTER A WITH ACUTE
    case '\u00C2':  //  Â LATIN CAPITAL LETTER A WITH CIRCUMFLEX
    // and so on for about 20 lines...
        return "A";
        break;

    case '\u00C6'://  Æ LATIN CAPITAL LIGATURE AE
        return "AE";
        break;

    // And so on for pages...
}

Estoy de acuerdo. Debe crear un diccionario de conversiones específicamente para su aplicación y audiencia esperada. Por ejemplo, para una audiencia de habla hispana solo traduciría ÁÉÍÓÚÜÑáéíóúü¡¡
Roberto Bonvallet

Roberto, hay miles de personajes y no puedo hacer este manual.
AhmetB - Google

2
¿Qué lenguaje humano estás usando que tiene "miles" de caracteres? ¿Japonés? ¿A qué esperarías que ど う し よ う と し て い ま す か se convierta?
Dour High Arch

66
El ejemplo que ha dado no es el ideal: U + 00DF LETRA PEQUEÑA LATINA S "ß" no es la misma letra Unicode que U + 03B2 LETRA PEQUEÑA GRIETA BETA "β".
Joachim Sauer

2

La siguiente clase hace el truco:

org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.