Перетворення символів, наголосних букв в англійський алфавіт


129

Проблема полягає в тому, що, як відомо, в діаграмі Unicode є тисячі символів, і я хочу перетворити всі подібні символи в літери, які є англійським алфавітом.

Наприклад, ось кілька конверсій:

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

і я побачив, що існує більше 20 версій букви A / a. і я не знаю, як їх класифікувати. Вони схожі на голки в копиці сіна.

Повний список символів unicode знаходиться на веб- сайті http://www.ssec.wisc.edu/~tomw/java/unicode.html або http://unicode.org/charts/charindex.html . Просто спробуйте прокрутити вниз і побачити варіанти літер.

Як я можу конвертувати все це за допомогою Java? Будь ласка, допоможи мені :(


Дивіться це запитання: stackoverflow.com/questions/249087/… - також мають бути інші запитання щодо цієї теми, але наразі я не можу їх знайти.
шнадер

1
Ваш третій приклад повинен бути Ȳ → Y?
Dour High Arch

2
Чому ти хочеш це робити? Якби ми знали, яка ваша загальна мета, ми могли б бути кориснішими.
Девід Торнлі

Девіде, ви знаєте, що деякі ЕМО використовують різні символи у реченнях. Ось вам приклад: ฬ. ¢. tђє ฬ ย η∂єg ¢ ค ק ђ Ŧ ค ๓ เ ℓy <- Розв’яжіть це :) @schnaader, я думаю, що саме це я шукаю, але не на Java.
AhmetB - Google

Ця розмова велася раніше - дивіться @schnaader вище.
dkretz

Відповіді:


197

Відновлення моєї публікації з Як видалити діакритику (акценти) із рядка в .NET?

Цей метод прекрасно працює в java (виключно для того, щоб видалити діакритичні позначки, акценти) .

Це в основному перетворює всі наголошені символи в їхні децентризовані аналоги з подальшим їх поєднанням діакритики. Тепер ви можете використовувати регулярний вираз, щоб зняти діакритику.

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

4
InCombiningDiacriticalMarks не конвертує всі кирилиці. Наприклад, Општина Богомила не торкається. Було б добре, якби можна було перетворити його на Опстіну Богомілу чи щось таке
iwein

13
Він зовсім не транслітерується. Він просто видаляє розкладені діакритичні позначки ("наголоси"). Попередній крок (Form.NFD) розбиває á на + ', тобто розкладає наголошений символ на знак без наголосу плюс діакритичний знак. Це перетворило б кирилицю на Ѡ, але не надалі.
MSalters

1
Джордж опублікував, що краще використовувати \\ p {IsM} замість \\ p {InCombiningDiacriticalMarks} на сайті glaforge.appspot.com/article/… Зауважте, що я його не перевіряв.
Аторрас

2
\\ p {IsM}, здається, не працює для іспанських наголосів, як á ó ú ñ é í. Навпаки, "\\ p {InCombiningDiacriticalMarks} + працює на це добре
Лоїк

Це працює не для всіх спеціальних символів - я подав неправильну проблему для Android, щоб дізнатися, що -> code.google.com/p/android/isissue/detail?id=189515 Хтось знає правильний спосіб це зробити?
Michał Tajchert

71

Це частина Apache Commons Lang від ver. 3.0.

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

повертає An

Також дивіться http://www.drillio.com/en/software-development/java/removing-accents-diacritics-in-any-language/


Це рішення дивовижне. Це працює і з грецькою! Дякую.
Том,

5
Це не ідеально для перекладу польських символів з ł і and відсутнє: вхід: ŚŻÓŁĄĆĘŹąółęąćńŃ вихід: SZOŁACEZaołeacnN
Роберт

1
Приємна утиліта, але оскільки її код точно такий, як показаний у прийнятій відповіді, і ви не хочете додавати залежність від Commons Lang, ви можете просто скористатися згаданим фрагментом.
поляретто

1
з apache, звичайним у моєму випадку: Đ не переходити на D
Hoang

@Hoang, Роберт, можливо, можливість надіслати запит на тягнення :)
Ondra Žižka

19

Спроба "перетворити їх усіх" - це неправильний підхід до проблеми.

По-перше, вам потрібно зрозуміти обмеження того, що ви намагаєтеся зробити. Як зазначають інші, діакритики є з причини: вони по суті є унікальними літерами в алфавіті цієї мови з власним значенням / звуком тощо. Видалення цих знаків - це те саме, що замінити випадкові букви англійським словом. Це ще до того, як ви навіть задумаєтесь про те, щоб розглянути кириличні мови та інші тексти на основі скриптів, такі як арабська, які просто неможливо "перетворити" на англійську.

Якщо вам доведеться з будь-якої причини перетворити символів, то єдиний розумний спосіб підійти до цього, щоб спочатку зменшити обсяг завдання, що знаходиться в роботі. Розглянемо джерело введення даних - якщо ви кодуєте додаток для "західного світу" (щоб використовувати як гарну фразу, як будь-яку), навряд чи вам колись знадобиться розбирати арабські символи. Аналогічно, набір символів Unicode містить сотні математичних та зображувальних символів: користувачам немає (простого) способу безпосередньо вводити їх, тому ви можете припустити, що їх можна ігнорувати.

Виконуючи ці логічні кроки, ви можете зменшити кількість можливих символів для розбору до тієї точки, де операція пошуку / заміни на базі словника здійснена. Потім це стає невеликою кількістю трохи нудної роботи, створюючи словники, і тривіальним завданням виконувати заміну. Якщо ваша мова підтримує нативні символи Unicode (як це робить Java) і оптимізує статичні структури правильно, такі знаходження та заміна, як правило, є сліпуче швидкими.

Це випливає з досвіду роботи над програмою, яка була необхідна для дозволу кінцевим користувачам шукати бібліографічні дані, що включають діакритичні символи. Масиви пошуку (як це було у нашому випадку) потребували, мабуть, 1 людини на день, щоб покрити всі діакритичні позначки для всіх західноєвропейських мов.


Дякую за відповідь. Насправді я не працюю з арабською мовою чи щось подібне. Ви знаєте, що деякі люди використовують діакритику як забавних персонажів, і я маю це зняти, наскільки я можу. Наприклад, я сказав у прикладі перетворення "tђє Ŧ ค ๓ เ ℓy -> Сім'я", але здається, що важко перетворити його повністю. Однак ми можемо зробити перетворення "òéışöç-> oeisoc" простим способом. Але який саме спосіб це зробити. Створення масивів та заміна вручну? Або ця мова має рідні функції щодо цього питання?
AhmetB - Google

15

Оскільки кодування, яке перетворює "Сімейство" на "tђє Ŧ ค ๓ เ ℓy", фактично є випадковим і не дотримується жодного алгоритму, який можна пояснити інформацією про кодові точки Unicode, не існує загального способу вирішити цей алгоритмічно.

Вам потрібно буде скласти відображення символів Unicode в латинські символи, які вони нагадують. Можливо, ви могли б зробити це за допомогою розумного машинного вивчення фактичних гліфів, що представляють кодові точки Unicode. Але я думаю, що зусиль для цього було б більше, ніж вручну побудувати це картографування. Особливо, якщо у вас є хороша кількість прикладів, з яких ви можете побудувати своє відображення.

Для уточнення: кілька підстановок насправді можна вирішити за допомогою даних Unicode (як показують інші відповіді), але деякі листи просто не мають розумної асоціації з латинськими символами, якими вони нагадують.

Приклади:

  • "ђ" (U + 0452 CYRILLIC SMALL LETTER DJE) більше пов'язаний з "d", ніж з "h", але використовується для позначення "h".
  • "Ŧ" (U + 0166 LATIN CAPITAL LETTER T With STROKE) дещо пов'язаний з "T" (як випливає з назви), але використовується для позначення "F".
  • "ค" (U + 0E04 ТАЙСЬКИЙ ХАРАКТЕР KHO KHWAI) взагалі не пов'язаний з жодним латинським символом, і у вашому прикладі використовується для позначення "a"

7

На оригінальний запит уже відповіли.

Однак я розміщую нижченаведену відповідь для тих, хто, можливо, шукає загальний код транслітерації для транслітерації будь-якої комірки на латинську / англійську мови на Java.

Наївне значення транслітерації: Переведена рядок у остаточній формі / цільовій діаграмі звучить як рядок у початковому вигляді. Якщо ми хочемо транслітерувати будь-яку діаграму на латинську (англійські алфавіти), то ICU4 (бібліотека ICU4J в java) зробить цю роботу.

Ось фрагмент коду в 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

Рядок перевірений: ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÌÍÎÏÑÒÓÔÕÖØÙÚÛÜÝß

Перевірено:

  • Вихід з Apache Commons Lang3 : AAAAAACECEEEIIIIÐNOOOOOØUUUUYß
  • Вихід з ICU4j : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUYß
  • Вихід з JUnidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUss (проблема з Ý та інша проблема )
  • Вихід з Unidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUYss

Останній вибір - найкращий.


1
@mehmet Просто слідкуйте за readme за адресою github.com/xuender/unidecode . Він повинен бути чимось на зразок Unidecode.decode ("BÁÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝß") після імпорту залежності.
кактусбрех

6

Якщо потрібно перетворити "òéışöç-> oeisoc", ви можете скористатися цим початковим моментом:

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 надає клас java.text.Normalizer, який можна використовувати для цього завдання.

Дивіться приклад тут


На жаль, це не поводиться з лігатурами, як Æ.
Dour High Arch

Цей метод особливо корисний, якщо вам потрібно по-різному виявляти та обробляти класи діакритики (тобто, уникати спеціальних символів у LaTeX).
vallismortis

4

Ви можете спробувати використовувати unidecode, який доступний як дорогоцінний камінь і як модуль Perl на cpan . По суті, він працює як величезна таблиця пошуку, де кожна точка коду unicode відноситься до символу або рядку ascii.


Ви можете отримати таблицю пошуку з одного з них.
Кеті Ван Стоун,

Це дивовижний пакет, але він транслітералізує звук персонажа, наприклад, він перетворює "北" на "Bei", тому що саме так звучить персонаж у мандарині. Я думаю, що запитувач хоче перетворити гліфи на те, що вони візуально нагадують англійською мовою.
Dour High Arch

Хоча це і для латинських символів. â стає a, та ін. @ahmetalpbalkan Я згоден з Кеті, ти можеш використовувати його як ресурс для створення власної таблиці пошуку, логіка повинна бути досить простою. На жаль, схоже, не існує версії Java.
Даніель Вандерслуйс

@ahmetalpbalkan Ось унікальний код для Java.
Якуб Жирутка

4

Немає простого або загального способу зробити те, що ви хочете, тому що це лише ваша суб'єктивна думка, що ці букви виглядають симпатичними літерами, які ви хочете перетворити. Насправді це окремі букви з власними чіткими назвами та звуками, які просто поверхово виглядають як латинська літера.

Якщо ви хочете цієї конверсії, вам слід створити власну таблицю перекладів на основі того, до яких латинських літер, на вашу думку, повинні бути перетворені не латинські літери.

(Якщо ви хочете видалити діакритичні позначки, у цій темі є кілька відповідей: Як видалити діакритику (акценти) із рядка в .NET? Однак ви описуєте більш загальну проблему)


+1. Ось версія Java на питання "видалити діакритику": stackoverflow.com/questions/1016955/… ; дивіться відповіді Майкла Боргвардта та девіо
Джонік

4

Я спізнююсь на вечірку, але після того, як сьогодні зіткнувся з цим питанням, я вважав цю відповідь дуже хорошою:

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

Довідка: https://stackoverflow.com/a/16283863


Невелике попередження - воно видаляє U + 00DF LATIN SMALL LETTER SHARP S "ß"
rafalmag

А також Æ ... До поганого.
кактусбребр

4

Проблема "перетворення" довільного Unicode в ASCII полягає в тому, що значення символу залежить від культури. Наприклад, "Я" для німецькомовної людини повинна бути перетворена в "ss", тоді як англомовний, мабуть, перетворив би її на "B".

Додайте до цього той факт, що Unicode має декілька кодових точок для одних і тих же гліфів.

Підсумок полягає в тому, що єдиний спосіб зробити це - створити масивну таблицю з кожним символом Unicode та символом ASCII, в який ви хочете перетворити його. Можна скористатися ярликом, нормалізуючи символи з наголосами до нормалізації форми KD, але не всі символи нормалізуються до ASCII. Крім того, Unicode не визначає, які частини гліфа є "акцентами".

Ось крихітний уривок із програми, яка робить це:

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...
}

Я згоден. Вам слід створити словник перетворень спеціально для вашої програми та очікуваної аудиторії. Наприклад, для іспаномовної аудиторії я переклав би лише ÁÉÍÓÚÜÑáéíóúü¿¡
Роберто Бонвальлет

Роберто є тисячі символів, і я не можу виконати цей посібник.
AhmetB - Google

2
Якою людською мовою ви користуєтеся, що має "тисячі" символів? Японський? У що ви очікуєте перетворення ど う し よ う と し い ま す す?
Dour High Arch

6
Приклад, який ви подали, не є ідеальним: U + 00DF ЛАТИНА МАЛЬКОГО ПІСЛЕННЯ SHARP S "ß" - це не та сама буква Unicode, що U + 03B2 GREEK SMALL LETTER BETA "β".
Йоахім Зауер

2

Наступний клас робить фокус:

org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.