Konwersja symboli, liter akcentujących na alfabet angielski

130

Problem polega na tym, że jak wiesz, na wykresie Unicode są tysiące znaków i chcę zamienić wszystkie podobne znaki na litery, które są w alfabecie angielskim.

Na przykład oto kilka konwersji:

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

i zobaczyłem, że istnieje ponad 20 wersji litery A / a. i nie wiem, jak je sklasyfikować. Wyglądają jak igły w stogu siana.

Pełna lista znaków Unicode znajduje się pod adresem http://www.ssec.wisc.edu/~tomw/java/unicode.html lub http://unicode.org/charts/charindex.html . Po prostu spróbuj przewinąć w dół i zobaczyć odmiany liter.

Jak mogę to wszystko przekonwertować za pomocą Java? Proszę pomóż mi :(

AhmetB - Google
źródło
Zobacz to pytanie: stackoverflow.com/questions/249087/… - powinno być też kilka innych pytań na ten temat, ale nie mogę ich w tej chwili znaleźć.
schnaader
1
Czy twoim trzecim przykładem powinno być Ȳ → Y?
Dour High Arch
2
Dlaczego chcesz to zrobić? Gdybyśmy wiedzieli, jaki jest Twój ogólny cel, moglibyśmy być bardziej pomocni.
David Thornley
David, wiesz, że niektóre EMO używają różnych znaków w zdaniach. Oto przykład: ฬ. ¢. tђє ฬ ย η∂єг ¢ ค ק ђ Ŧ ค ๓ เ ℓy <- Rozwiąż to :) @schnaader, myślę, że tego właśnie szukam, ale nie w Javie.
AhmetB - Google
Ta rozmowa odbyła się już wcześniej - zobacz @schnaader powyżej.
dkretz

Odpowiedzi:

199

Ponowne publikowanie mojego posta z Jak usunąć znaki diakrytyczne (akcenty) z ciągu znaków w .NET?

Ta metoda działa dobrze w Javie (wyłącznie w celu usunięcia znaków diakrytycznych, czyli aka) .

Zasadniczo konwertuje wszystkie akcentowane znaki na ich pozbawione akcentów odpowiedniki, a następnie ich łączące znaki diakrytyczne. Teraz możesz użyć wyrażenia regularnego, aby usunąć znaki diakrytyczne.

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("");
}
hashable
źródło
4
InCombiningDiacriticalMarks nie konwertuje wszystkich cyrylicy. Na przykład Општина Богомила jest nietknięta. Byłoby miło, gdyby można było zamienić go na
Opstinę Bogomiłę
13
W ogóle nie dokonuje transliteracji. Usuwa jedynie rozłożone znaki diakrytyczne („akcenty”). W poprzednim kroku (Form.NFD) następuje rozbicie á na + ', tj. Dekomponowanie znaku akcentowanego na znak bez akcentu oraz znak diakrytyczny. Spowoduje to zamianę cyrylicy Ѽ na Ѡ, ale nie dalej.
MSalters
1
George napisał, że lepiej byłoby użyć \\ p {IsM} zamiast \\ p {InCombiningDiacriticalMarks} na glaforge.appspot.com/article/ ... Zauważ, że nie testowałem tego.
ATorras
2
\\ p {IsM} nie wydaje się działać z hiszpańskimi akcentami, takimi jak á ó ú ñ é í. Wręcz przeciwnie, „\\ p {InCombiningDiacriticalMarks} + działa dobrze na to
Loic
To nie działa dla wszystkich znaków specjalnych - wysłałem niewłaściwy problem dla Androida, aby się tego dowiedzieć -> code.google.com/p/android/issues/detail?id=189515 Czy ktoś wie, jak to zrobić?
Michał Tajchert
71

Jest częścią Apache Commons Lang od wer. 3.0.

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

zwroty An

Zobacz także http://www.drillio.com/en/software-development/java/removing-accents-diacritics-in-any-language/

Ondra Žižka
źródło
To rozwiązanie jest niesamowite. Działa też z greckim! Dziękuję Ci.
Tom
5
Nie jest idealne do tłumaczenia polskich znaków z ł i Ł brakuje: input: ŚÓŁĄĆĘŹąółęąćńŃ output: SZOŁACEZaołeacnN
Robert
1
Niezłe narzędzie, ale ponieważ jego kod jest dokładnie taki sam, jak ten pokazany w zaakceptowanej odpowiedzi, a nie chcesz dodawać zależności od Commons Lang, możesz po prostu użyć wspomnianego wcześniej fragmentu.
polaretto
1
z apache powszechnym w moim przypadku: Đ nie konwertuj na D
Hoang
@Hoang, Robert może mieć szansę na wysłanie pull requesta :)
Ondra Žižka
19

Próba „przekonwertowania ich wszystkich” jest niewłaściwym podejściem do problemu.

Po pierwsze, musisz zrozumieć ograniczenia tego, co próbujesz zrobić. Jak inni zauważyli, znaki diakrytyczne istnieją z jakiegoś powodu: są to zasadniczo unikalne litery w alfabecie tego języka z własnym znaczeniem / dźwiękiem itp.: Usuwanie tych znaków jest tym samym, co zastępowanie przypadkowych liter w angielskim słowie. To jest jeszcze zanim przejdziesz do rozważenia języków cyrylicy i innych tekstów opartych na pismach, takich jak arabski, których po prostu nie da się „przekonwertować” na angielski.

Jeśli z jakiegoś powodu musisz przekonwertować postacie, to jedyny rozsądny sposób podejścia do tego, aby najpierw zmniejszyć zakres wykonywanego zadania. Weź pod uwagę źródło danych wejściowych - jeśli kodujesz aplikację dla „świata zachodniego” (aby użyć tak dobrego wyrażenia, jak inne), byłoby mało prawdopodobne, że kiedykolwiek będziesz musiał analizować znaki arabskie. Podobnie zestaw znaków Unicode zawiera setki symboli matematycznych i obrazkowych: nie ma (łatwego) sposobu, aby użytkownicy mogli je bezpośrednio wprowadzić, więc można założyć, że można je zignorować.

Wykonując te logiczne kroki, można zmniejszyć liczbę możliwych znaków do przeanalizowania do punktu, w którym operacja wyszukiwania / zamiany w oparciu o słownik jest wykonalna. Tworzenie słowników staje się wtedy niewielką ilością nieco nudnej pracy, a ich zastąpienie staje się banalnym zadaniem. Jeśli twój język obsługuje natywne znaki Unicode (tak jak Java) i poprawnie optymalizuje struktury statyczne, takie wyszukiwanie i zamiany są zwykle niezwykle szybkie.

Wynika to z doświadczenia związanego z pracą nad aplikacją, która była wymagana, aby umożliwić użytkownikom końcowym wyszukiwanie danych bibliograficznych zawierających znaki diakrytyczne. Stworzenie tablic wyszukiwania (tak jak to było w naszym przypadku) zajęło około 1 osobodni, aby pokryć wszystkie znaki diakrytyczne dla wszystkich języków zachodnioeuropejskich.

Ian
źródło
Dziękuję za odpowiedź. Właściwie nie pracuję z językami arabskimi ani z czymś takim. Wiesz, że niektórzy ludzie używają znaków diakrytycznych jako zabawnych znaków i muszę je usunąć tak bardzo, jak tylko mogę. Na przykład, powiedziałem w przykładzie konwersja „tђє Ŧ ค ๓ เ ℓy -> Rodzina”, ale wydaje się, że jest to trudne. Możemy jednak dokonać konwersji „òéışöç-> oeisoc” w prosty sposób. Ale jaki jest dokładny sposób, aby to zrobić. Tworzysz tablice i zastępujesz je ręcznie? A może ten język ma natywne funkcje dotyczące tego problemu?
AhmetB - Google
15

Ponieważ kodowanie, które zamienia „rodzinę” na „tђє Ŧ ค ๓ เ ℓy” jest w rzeczywistości losowe i nie jest zgodne z żadnym algorytmem, który można wyjaśnić na podstawie informacji o zawartych w nim punktach kodowych Unicode, nie ma ogólnego sposobu rozwiązania tego algorytmu.

Będziesz musiał zbudować mapowanie znaków Unicode na znaki łacińskie, które przypominają. Prawdopodobnie możesz to zrobić za pomocą inteligentnego uczenia maszynowego na rzeczywistych glifach reprezentujących punkty kodowe Unicode. Myślę jednak, że wysiłek związany z tym byłby większy niż ręczne tworzenie tego mapowania. Zwłaszcza jeśli masz dużą liczbę przykładów, z których możesz zbudować swoje mapowanie.

Dla wyjaśnienia: kilka podstawień można faktycznie rozwiązać za pomocą danych Unicode (jak pokazują inne odpowiedzi), ale niektóre litery po prostu nie mają rozsądnego związku z łacińskimi znakami, które przypominają.

Przykłady:

  • „ђ” (U + 0452 CYRILLIC MAŁA LITERA DJE) jest bardziej związany z „d” niż z „h”, ale jest używany do reprezentowania „h”.
  • „Ŧ” (U + 0166 WIELKA LITERA ŁACIŃSKA T Z UDOKIEM) jest w pewnym sensie spokrewniony z „T” (jak sugeruje nazwa), ale jest używany do reprezentowania „F”.
  • „ค” (U + 0E04 THAI CHARACTER KHO KHWAI) nie jest w ogóle powiązany z żadnym łacińskim znakiem, a w Twoim przykładzie jest używany do reprezentowania „a”
Joachim Sauer
źródło
7

Odpowiedź na pierwotną prośbę została już udzielona.

Jednak zamieszczam poniższą odpowiedź dla tych, którzy mogą szukać ogólnego kodu transliteracji do transliteracji dowolnego zestawu znaków na łacinę / angielski w Javie.

Naiwne znaczenie tranliteracji: przetłumaczony ciąg w swojej ostatecznej formie / docelowym zestawie znaków brzmi jak ciąg w swojej oryginalnej formie. Jeśli chcemy dokonać transliteracji dowolnego zestawu znaków na łacinę (alfabety angielskie), wtedy ICU4 (biblioteka ICU4J w java) zrobi to.

Oto fragment kodu w 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
źródło
7

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

Przetestowano:

  • Dane wyjściowe z Apache Commons Lang3 : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUUYß
  • Wyjście z ICU4j : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUUYß
  • Wyjście z JUnidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUUss (problem z Ý i inny problem )
  • Wyjście z Unidecode : AAAAAAECEEEEIIIIDNOOOOUUUUUYss

Ostatni wybór jest najlepszy.

kaktusowy
źródło
1
@mehmet Po prostu postępuj zgodnie z readme na github.com/xuender/unidecode . Po zaimportowaniu zależności powinno to być coś w rodzaju Unidecode.decode ("ÁÂÃĘÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝß").
cactuschibre
6

Jeśli potrzebujesz przekonwertować „òéışöç-> oeisoc”, możesz użyć tego jako punktu wyjścia:

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 udostępnia klasę java.text.Normalizer, której można użyć do tego zadania.

Zobacz przykład tutaj

RealHowTo
źródło
Niestety to nie poradzi sobie z ligaturami takimi jak Æ.
Dour High Arch
Ta metoda jest szczególnie użyteczna, jeśli musisz inaczej wykrywać i obsługiwać klasy znaków diakrytycznych (np. Ucieczki znaków specjalnych w LaTeX).
vallismortis
4

Możesz spróbować użyć unidecode, który jest dostępny jako rubinowy klejnot i jako moduł perla na cpan . Zasadniczo działa jako ogromna tabela wyszukiwania, w której każdy punkt kodu Unicode odnosi się do znaku lub ciągu znaków ascii.

Daniel Vandersluis
źródło
Możesz uzyskać tabelę wyszukiwania z jednego z nich.
Kathy Van Stone
To niesamowity pakiet, ale transliteruje dźwięk znaku, na przykład konwertuje „北” na „Bei”, ponieważ tak właśnie brzmi znak w języku mandaryńskim. Myślę, że pytający chce przekonwertować glify na to, co wizualnie przypominają w języku angielskim.
Dour High Arch
Robi to jednak w przypadku postaci łacińskich. â staje się a, et al. @ahmetalpbalkan Zgadzam się z Kathy, możesz użyć go jako źródła do zbudowania własnej tabeli odnośników, logika powinna być dość prosta. Niestety nie ma wersji java.
Daniel Vandersluis
@ahmetalpbalkan Oto unidecode dla Javy.
Jakub Jirutka
4

Nie ma łatwego ani ogólnego sposobu na zrobienie tego, co chcesz, ponieważ jest to tylko twoja subiektywna opinia, że ​​te litery wyglądają tak, jak litery łacińskie, na które chcesz przekonwertować. W rzeczywistości są to oddzielne litery z własnymi, odrębnymi nazwami i dźwiękami, które po prostu wyglądają jak litera łacińska.

Jeśli chcesz tej konwersji, musisz utworzyć własną tabelę tłumaczeń na podstawie tego, na jakie litery łacińskie powinny zostać przekonwertowane litery niełacińskie.

(Jeśli chcesz usunąć tylko znaki diakrytyczne, w tym wątku jest kilka odpowiedzi: Jak usunąć znaki diakrytyczne (akcenty) z ciągu znaków w .NET? Jednak opisujesz bardziej ogólny problem)

JacquesB
źródło
+1. Oto wersja Java pytania „usuń znaki diakrytyczne”: stackoverflow.com/questions/1016955/… ; zobacz odpowiedzi Michaela Borgwardta i devio
Jonik
4

Spóźniłem się na imprezę, ale po dzisiejszym zmierzeniu się z tym problemem uznałem, że ta odpowiedź jest bardzo dobra:

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

Źródła: https://stackoverflow.com/a/16283863

Francisco Junior
źródło
Małe ostrzeżenie - usuwa U + 00DF ŁACIŃSKI MAŁA LITERA OSTRA S "ß"
rafalmag
A także Æ ... Źle.
cactuschibre
4

Problem z „konwersją” dowolnego Unicode do ASCII polega na tym, że znaczenie znaku jest zależne od kultury. Na przykład „ß” na osobę niemieckojęzyczną powinno zostać zamienione na „ss”, podczas gdy osoba anglojęzyczna prawdopodobnie zamieniłaby to na „B”.

Dodaj do tego fakt, że Unicode ma wiele punktów kodowych dla tych samych glifów.

W rezultacie jedynym sposobem na to jest utworzenie ogromnej tabeli z każdym znakiem Unicode i znakiem ASCII, na który chcesz go przekonwertować. Możesz skorzystać z skrótu, normalizując znaki z akcentami do normalizacji z KD, ale nie wszystkie znaki normalizują się do ASCII. Ponadto Unicode nie definiuje, które części glifu są „akcentami”.

Oto mały fragment aplikacji, która to robi:

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...
}
Dour High Arch
źródło
Zgadzam się. Należy utworzyć słownik konwersji specjalnie dla swojej aplikacji i oczekiwanych odbiorców. Na przykład dla publiczności hiszpańskojęzycznej przetłumaczyłbym tylko ÁÉÍÓÚÜÑáéíóúü¿¡
Roberto Bonvallet
Roberto są tysiące postaci i nie mogę zrobić tego podręcznika.
AhmetB - Google
2
Jakim ludzkim językiem używasz „tysięcy” znaków? Język japoński? Na co można by się spodziewać konwersji ど う し よ う と し て い ま す?
Dour High Arch
6
Podany przykład nie jest idealny: U + 00DF ŁACIŃSKA MAŁA LITERA OSTRA S „ß” to nie ta sama litera Unicode, co U + 03B2 GREEK MAŁA LITERA BETA „β”.
Joachim Sauer
2

Następująca klasa załatwia sprawę:

org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter
TomWolk
źródło