Convertendo símbolos, letras de acento para alfabeto inglês

129

O problema é que, como você sabe, existem milhares de caracteres no gráfico Unicode e desejo converter todos os caracteres semelhantes nas letras que estão no alfabeto inglês.

Por exemplo, aqui estão algumas conversões:

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

e vi que existem mais de 20 versões da letra A / a. e eu não sei como classificá-los. Eles parecem agulhas no palheiro.

A lista completa de caracteres unicode está em http://www.ssec.wisc.edu/~tomw/java/unicode.html ou http://unicode.org/charts/charindex.html . Apenas tente rolar para baixo e ver as variações de letras.

Como posso converter tudo isso com Java? Por favor me ajude :(

AhmetB - Google
fonte
Veja esta pergunta: stackoverflow.com/questions/249087/… - também deve haver outras perguntas sobre esse tópico, mas não consigo encontrá-las no momento.
schnaader
1
Seu terceiro exemplo deve ser Ȳ → Y?
Dour High Arch
2
Por que você quer fazer isso? Se soubéssemos qual era seu objetivo geral, poderíamos ser mais úteis.
David Thornley
David, você sabe que alguns EMOs usam caracteres diferentes nas frases. Aqui está um exemplo: ฬ. ¢. tђє ฬ ย η∂єг ¢ ค ק ђ ค ๓ <y <- Resolva isso :) @schnaader, acho que é isso que estou procurando, mas não em Java.
AhmetB - Google
Essa conversa já foi feita antes - veja @schnaader acima.
21710 dkretz #

Respostas:

197

Reposicionando minha postagem de Como removo sinais diacríticos (acentos) de uma string no .NET?

Este método funciona bem em java (puramente com a finalidade de remover marcas diacríticas, também conhecidas como acentos) .

Basicamente, converte todos os caracteres acentuados em seus equivalentes descentralizados, seguidos pelos diacríticos combinados. Agora você pode usar uma regex para retirar os sinais 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("");
}
lavável
fonte
4
InCombiningDiacriticalMarks não converte todos os cirílicos. Por exemplo, Општина Богомила está intocado. Seria bom se alguém pudesse convertê-lo para Opstina Bogomila ou algo
Iwein
13
Não é transliterado. Apenas remove marcas diacríticas decompostas ("acentos"). O passo anterior (Form.NFD) divide á em + ', ou seja, decompõe o caractere acentuado em um caractere não acentuado mais uma marca diacrítica. Isso converteria cirílico Ѽ em Ѡ, mas não mais.
MSalters
1
George postou que poderia ser melhor usar o \\ p {IsM} em vez de \\ p {InCombiningDiacriticalMarks} em glaforge.appspot.com/article/… Observe que eu não o testei.
ATorras 26/03
2
\\ p {IsM} parece não funcionar com sotaques espanhóis como á ó ú ñ é í. Pelo contrário, "\\ p {InCombiningDiacriticalMarks} + está funcionando bem para isso
Loic
Ele não funciona para todos os caracteres especiais - enviei um problema errado para o Android para saber que -> code.google.com/p/android/issues/detail?id=189515 Alguém sabe a maneira correta de fazer isso?
Michał Tajchert
71

É uma parte do Apache Commons Lang a partir do ver. 3.0

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

retorna An

Consulte também http://www.drillio.com/en/software-development/java/removing-accents-diacritics-in-any-language/

Ondra Žižka
fonte
Esta solução é incrível. Também funciona com grego! Obrigado.
Tom
5
Não é perfeito para a tradução de caracteres poloneses de ł e Ł está faltando: input: ŚŻÓŁĄĆĘŹąółęąćńŃ output: SZOŁACEZaołeacnN
Robert
1
Utilitário agradável, mas como o código é exatamente o mesmo que o mostrado na resposta aceita, e você não deseja adicionar uma dependência ao Commons Lang, basta usar o trecho de código acima mencionado.
polaretto
1
com apache comum no meu caso: DJ não converter para D
Hoang
@Hoang, Robert talvez a oportunidade de enviar uma solicitação de recebimento :)
Ondra Žižka
19

Tentar "convertê-los todos" é a abordagem errada para o problema.

Primeiro, você precisa entender as limitações do que você está tentando fazer. Como outros salientaram, os diacríticos existem por uma razão: são essencialmente letras únicas no alfabeto dessa língua com seu próprio significado / som, etc .: remover essas marcas é o mesmo que substituir letras aleatórias em uma palavra em inglês. Isso é antes mesmo de você considerar os idiomas cirílicos e outros textos baseados em scripts, como o árabe, que simplesmente não podem ser "convertidos" para o inglês.

Se , por qualquer motivo, você precisar converter caracteres, a única maneira sensata de abordar isso é reduzir primeiro o escopo da tarefa em questão. Considere a fonte da entrada - se você estiver codificando um aplicativo para "o mundo ocidental" (para usar uma frase tão boa quanto qualquer outra), é improvável que você precise analisar caracteres árabes. Da mesma forma, o conjunto de caracteres Unicode contém centenas de símbolos matemáticos e pictóricos: não existe uma maneira (fácil) de os usuários inseri-los diretamente, portanto, você pode assumir que eles podem ser ignorados.

Ao seguir estas etapas lógicas, você pode reduzir o número de caracteres possíveis para analisar até o ponto em que uma operação de pesquisa / substituição baseada em dicionário é viável. Em seguida, torna-se uma pequena quantidade de trabalho um pouco chato, criando os dicionários, e uma tarefa trivial para executar a substituição. Se o seu idioma suportar caracteres Unicode nativos (como o Java faz) e otimizar estruturas estáticas corretamente, essas localizações e substituições tendem a ser incrivelmente rápidas.

Isso resulta da experiência de ter trabalhado em um aplicativo necessário para permitir que os usuários finais pesquisassem dados bibliográficos que incluíam caracteres diacríticos. As matrizes de pesquisa (como no nosso caso) levaram talvez um dia para serem produzidas, para cobrir todas as marcas diacríticas de todas as línguas da Europa Ocidental.

Ian
fonte
Obrigado por responder. Na verdade, não estou trabalhando com idiomas árabes ou algo assim. Você sabe que algumas pessoas usam os diacríticos como personagens engraçados e eu tenho que remover isso o máximo que posso. Por exemplo, eu disse "tђє Ŧ ค ๓ เy -> the Family" conversão no exemplo, mas parece difícil convertê-lo completamente. No entanto, podemos fazer a conversão "òéışöç-> oeisoc" de uma maneira simples. Mas qual é a maneira exata de fazer isso. Criando matrizes e substituindo manualmente? Ou esse idioma possui funções nativas sobre esse problema?
AhmetB - Google
15

Como a codificação que transforma "a Família" em "tђє Ŧ ค ๓ เ ℓy" é efetivamente aleatória e não segue nenhum algoritmo que possa ser explicado pelas informações dos pontos de código Unicode envolvidos, não há uma maneira geral de resolver isso algoritmicamente.

Você precisará criar o mapeamento de caracteres Unicode em caracteres latinos com os quais eles se assemelham. Você provavelmente poderia fazer isso com algum aprendizado de máquina inteligente sobre os glifos reais que representam os pontos de código Unicode. Mas acho que o esforço para isso seria maior do que construir manualmente esse mapeamento. Especialmente se você tiver uma boa quantidade de exemplos a partir dos quais poderá criar seu mapeamento.

Para esclarecer: algumas substituições podem realmente ser resolvidas através dos dados Unicode (como as outras respostas demonstram), mas algumas letras simplesmente não têm associação razoável com os caracteres latinos com os quais se parecem.

Exemplos:

  • "ђ" (U + 0452 CYRILLIC SMALL LETTER DJE) está mais relacionado a "d" do que a "h", mas é usado para representar "h".
  • "Ŧ" (U + 0166 LETRA EM CAPITAL LATINA T COM CURSO) está um pouco relacionado a "T" (como o nome sugere), mas é usado para representar "F".
  • "ค" (U + 0E04 PERSONAGEM TAILANDESA KHO KHWAI) não está relacionado a nenhum caractere latino e, no seu exemplo, é usado para representar "a"
Joachim Sauer
fonte
7

A solicitação original já foi respondida.

No entanto, estou postando a resposta abaixo para aqueles que possam estar procurando por código de transliteração genérico para transliterar qualquer conjunto de caracteres para latim / inglês em Java.

Significado ingênuo da tranliteração: a string traduzida em sua forma final / conjunto de caracteres de destino soa como a string em sua forma original. Se quisermos transliterar qualquer conjunto de caracteres para o latim (alfabetos em inglês), o ICU4 (biblioteca ICU4J em java) fará o trabalho.

Aqui está o trecho de código em 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;
    }
Dayanand Gowda
fonte
7

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

Testado:

  • Saída do Apache Commons Lang3: AAAAAÆCEEEEIIIIÐNOOOOOØUUUUYß
  • Saída do ICU4j : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUYß
  • Saída do JUnidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUUss (problema com Ý e outro problema )
  • Saída do Unidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUYss

A última escolha é a melhor.

cactuschibre
fonte
1
@mehmet Basta seguir o leia-me em github.com/xuender/unidecode . Deve ser algo como Unidecode.decode ("ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝß") depois de importar a dependência.
Cactuschibre 17/08/19
6

Se for necessário converter "òéışöç-> oeisoc", você pode usar isso como um ponto 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
    }
}

O JDK 1.6 fornece a classe java.text.Normalizer que pode ser usada para esta tarefa.

Veja um exemplo aqui

RealHowTo
fonte
Infelizmente isso não vai lidar com ligaduras como Æ.
Dour High Arch
Este método é particularmente útil se você precisar detectar e manipular classes de sinais diacríticos de maneira diferente (por exemplo, escapando de caracteres especiais no LaTeX).
Vallismortis
4

Você pode tentar usar unidecode, que está disponível como uma gema de rubi e como um módulo perl no cpan . Essencialmente, ele funciona como uma enorme tabela de pesquisa, na qual cada ponto de código unicode se relaciona a um caractere ou string ascii.

Daniel Vandersluis
fonte
Você pode obter uma tabela de pesquisa de uma delas.
Kathy Van Stone
Este é um pacote incrível, mas translitera o som do personagem, por exemplo, converte "北" em "Bei" porque é assim que o personagem soa no mandarim. Acho que o questionador quer converter glifos para o que eles se assemelham visualmente em inglês.
Dour High Arch
Faz isso para caracteres latinos, no entanto. â torna-se a, et al. @ahmetalpbalkan Concordo com Kathy, você pode usá-lo como um recurso para criar sua própria tabela de pesquisa, a lógica deve ser bem simples. Infelizmente, não parece haver uma versão java.
Daniel Vandersluis
@ahmetalpbalkan Aqui está o unidecode para Java.
Jakub Jirutka
4

Não há uma maneira fácil ou geral de fazer o que você deseja, porque é apenas a sua opinião subjetiva de que essas letras parecem ser as letras latinas nas quais você deseja converter. Na verdade, são letras separadas com seus próprios nomes e sons distintos, que superficialmente parecem uma letra latina.

Se você deseja essa conversão, deve criar sua própria tabela de tradução com base em quais letras latinas você acha que as letras não latinas devem ser convertidas.

(Se você deseja remover apenas marcas diacritiais, há algumas respostas neste segmento: Como remover diacríticos (acentos) de uma cadeia de caracteres no .NET? No entanto, você descreve um problema mais geral)

JacquesB
fonte
+1. Aqui está uma versão em Java da pergunta 'remover diacríticos': stackoverflow.com/questions/1016955/… ; veja as respostas de Michael Borgwardt e devio de
Jonik
4

Estou atrasado para a festa, mas depois de enfrentar esse problema hoje, achei esta resposta muito boa:

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

Referência: https://stackoverflow.com/a/16283863

Francisco Junior
fonte
Pequeno aviso - ele remove U + 00DF a minúsculo SHARP S "ß"
rafalmag
E também ... Para mal.
Cactuschibre 12/04
4

O problema com a "conversão" arbitrária de Unicode para ASCII é que o significado de um caractere depende da cultura. Por exemplo, “ß” para uma pessoa que fala alemão deve ser convertido para "ss", enquanto um falante de inglês provavelmente o converterá em "B".

Acrescente a isso o fato de o Unicode ter vários pontos de código para os mesmos glifos.

O resultado é que a única maneira de fazer isso é criar uma tabela enorme com cada caractere Unicode e o caractere ASCII no qual você deseja convertê-lo. Você pode usar um atalho normalizando caracteres com acentos no formulário de normalização KD, mas nem todos os caracteres normalizam para ASCII. Além disso, o Unicode não define quais partes de um glifo são "acentos".

Aqui está um pequeno trecho de um aplicativo que faz isso:

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...
}
Arco Alto de Dour
fonte
Concordo. Você deve criar um dicionário de conversões especificamente para seu aplicativo e público-alvo. Por exemplo, para um público de língua espanhola, eu traduziria apenas ÁÉÍÓÚÜÑáéíóúü¡¡
Roberto Bonvallet
Roberto, existem milhares de caracteres e eu não posso fazer este manual.
AhmetB - Google
2
Que linguagem humana você está usando que tem "milhares" de caracteres? Japonês? Em que você esperaria que ど う し よ う と し い ま す か fosse convertido?
40550 High Arco Dour
6
O exemplo que você deu não é o ideal: U + 00DF LETRA PEQUENA LATINA SHARP S "ß" não é a mesma letra Unicode que U + 03B2 LETRA PEQUENA GREGA BETA "β".
Joachim Sauer
2

A classe a seguir faz o truque:

org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter
TomWolk
fonte