Преобразование символов и букв с ударением в английский алфавит

129

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

Например, вот несколько преобразований:

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

и я увидел, что существует более 20 вариантов буквы А / а. и я не знаю, как их классифицировать. Они похожи на иголки в стоге сена.

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

Как мне все это преобразовать с помощью Java? Пожалуйста, помогите мне :(

AhmetB - Google
источник
См. Этот вопрос: stackoverflow.com/questions/249087/… - также должны быть другие вопросы по этой теме, но я не могу их найти в данный момент.
schnaader
1
Ваш третий пример должен быть Ȳ → Y?
Dour High Arch
2
почему ты хочешь сделать это? Если бы мы знали, какова ваша общая цель, мы могли бы быть более полезными.
Дэвид Торнли
Дэвид, ты знаешь, что некоторые эмо используют разные символы в предложениях. Вот вам пример: ฬ. ¢. tђє ฬ ย η∂єг ¢ ค ק ђ Ŧ ค ๓ เ ℓ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("");
}
hashable
источник
4
InCombiningDiacriticalMarks конвертирует не всю кириллицу. Например Општина Богомила нетронутая. Было бы неплохо, если бы можно было преобразовать его в
Опстину Богомилу
13
Он вообще не транслитерирует. Он просто удаляет разложенные диакритические знаки («акценты»). Предыдущий шаг (Form.NFD) разбивает á на + ', т. Е. Разлагает диакритический знак на безударный знак и диакритический знак. Это преобразовало бы кириллицу в Ѡ, но не дальше.
MSalters
1
Джордж сообщил, что было бы лучше использовать \\ p {IsM} вместо \\ p {InCombiningDiacriticalMarks} на glaforge.appspot.com/article/… Обратите внимание, что я не тестировал это.
ATorras
2
\\ p {IsM}, похоже, не работает с испанскими акцентами типа á ó ú ñ é í. Напротив, "\\ p {InCombiningDiacriticalMarks} + хорошо работает для этого"
Лоик
Это не работает для всех специальных символов - я отправил неправильную проблему для Android, чтобы узнать об этом -> code.google.com/p/android/issues/detail?id=189515 Кто-нибудь знает правильный способ сделать это?
Michał Tajchert,
71

Это часть Apache Commons Lang с вер. 3.0.

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

возвращается An

См. Также http://www.drillio.com/en/software-development/java/removing-accents-diacritics-in-any-language/

Ондра Жижка
источник
Это потрясающее решение. Он работает и с греческим! Спасибо.
Tom
5
Это не идеально для перевода польских символов из ł и отсутствует Ł: ввод: ŚŻÓŁĄĆĘŹąółęąćńŃ вывод: SZOŁACEZaołeacnN
Роберт
1
Хорошая утилита, но поскольку ее код точно такой же, как и в принятом ответе, и вы не хотите добавлять зависимость от Commons Lang, вы можете просто использовать вышеупомянутый фрагмент.
Polaretto
1
с обычным apache в моем случае: Đ не конвертировать в D
Hoang
@Hoang, Роберт, может быть, шанс отправить запрос
Ондра Жижка,
19

Попытка «преобразовать их всех» - неправильный подход к проблеме.

Во-первых, вам нужно понимать ограничения того, что вы пытаетесь сделать. Как отмечали другие, диакритические знаки существуют по какой-то причине: они, по сути, являются уникальными буквами в алфавите этого языка со своим собственным значением / звуком и т. Д.: Удаление этих знаков аналогично замене случайных букв в английском слове. Это еще до того, как вы даже перейдете к рассмотрению кириллических языков и других текстов, основанных на алфавите, таких как арабский, которые просто невозможно «преобразовать» в английский.

Если вы должны , по какой - либо причине, преобразование символов, то единственный разумный способ приблизиться к этому , что, во - первых сократить масштабы задачи. Рассмотрим источник ввода - если вы пишете приложение для «западного мира» (чтобы использовать такую ​​же хорошую фразу, как любая другая), маловероятно, что вам когда-либо понадобится разбирать арабские символы. Точно так же набор символов Unicode содержит сотни математических и графических символов: для пользователей нет (простого) способа напрямую ввести их, поэтому вы можете предположить, что их можно игнорировать.

Выполняя эти логические шаги, вы можете уменьшить количество возможных символов для синтаксического анализа до точки, в которой возможна операция поиска / замены на основе словаря. Тогда создание словарей превращается в небольшую скучную работу, а выполнение замены - в тривиальную задачу. Если ваш язык поддерживает собственные символы Unicode (как это делает Java) и правильно оптимизирует статические структуры, такой поиск и замена будут очень быстрыми.

Это связано с опытом работы над приложением, которое требовалось для того, чтобы конечные пользователи могли выполнять поиск в библиографических данных, содержащих диакритические символы. Для создания поисковых массивов (как это было в нашем случае) потребовалось около 1 человеко-дня, чтобы охватить все диакритические знаки для всех западноевропейских языков.

Ян
источник
Спасибо за ответ. На самом деле я не работаю с арабскими языками или чем-то в этом роде. Вы знаете, что некоторые люди используют диакритические знаки как забавные символы, и я должен удалить их, насколько это возможно. Например, я сказал преобразование «tђє Ŧ ค ๓ y -> the Family» в примере, но это кажется трудным полностью преобразовать его. Однако мы можем сделать преобразование «òéışöç-> oeisoc» простым способом. Но как именно это сделать? Создание массивов и замена вручную? Или у этого языка есть собственные функции по этой проблеме?
AhmetB - Google,
15

Поскольку кодирование, которое превращает «Семейство» в «tђє Ŧ ค ๓ เ ℓy», является фактически случайным и не следует никакому алгоритму, который можно объяснить информацией задействованных кодовых точек Unicode, нет общего способа решить эту проблему алгоритмически.

Вам нужно будет построить отображение символов Unicode в латинские символы, которые они похожи. Вероятно, вы могли бы сделать это с помощью интеллектуального машинного обучения на реальных глифах, представляющих кодовые точки Unicode. Но я думаю, что для этого потребуется больше усилий, чем при построении карты вручную. Особенно, если у вас есть большое количество примеров, на основе которых вы можете построить свое отображение.

Чтобы уточнить: некоторые из замен фактически могут быть решены с помощью данных Unicode (как показывают другие ответы), но некоторые буквы просто не имеют разумной связи с латинскими символами, на которые они похожи.

Примеры:

  • «ђ» (U + 0452 КИРИЛИЧЕСКАЯ СТРОЧНАЯ БУКВА DJE) больше относится к «d», чем к «h», но используется для обозначения «h».
  • «Ŧ» (U + 0166 ЛАТИНСКАЯ ЗАГЛАВНАЯ БУКВА T С ИНСУЛЬТОМ) отчасти связано с «T» (как следует из названия), но используется для обозначения «F».
  • «ค» (U + 0E04 THAI CHARACTER KHO KHWAI) вообще не связано с каким-либо латинским символом и в вашем примере используется для обозначения «а»
Иоахим Зауэр
источник
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 : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUYß
  • Вывод из ICU4j : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUYß
  • Выход из JUnidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUss (проблема с Ý и другая проблема )
  • Вывод из Unidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUYss

Последний вариант - лучший.

cactuschibre
источник
1
@mehmet Просто следуйте инструкциям на странице github.com/xuender/unidecode . После импорта зависимости это должно быть что-то вроде Unidecode.decode ("ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝß").
cactuschibre
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, который можно использовать для этой задачи.

См. Пример здесь

RealHowTo
источник
К сожалению, это не работает с лигатурами типа Æ.
Dour High Arch
Этот метод особенно полезен, если вам нужно по-разному обнаруживать и обрабатывать классы диакритических знаков (например, экранировать специальные символы в LaTeX).
vallismortis 01
4

Вы можете попробовать использовать unidecode, который доступен как ruby gem и как модуль perl на cpan . По сути, он работает как огромная таблица поиска, где каждая кодовая точка Unicode связана с символом или строкой ascii.

Даниэль Вандерслуис
источник
Возможно, вы сможете получить таблицу подстановки из одного из них.
Кэти Ван Стоун,
Это замечательный пакет, но он транслитерирует звук персонажа, например, он преобразует «北» в «Bei», потому что именно так персонаж звучит на мандаринском диалекте. Я думаю, что задающий вопрос хочет преобразовать глифы в то, что они визуально напоминают в английском языке.
Dour High Arch
Однако это действительно так для латинских символов. â становится a, et al. @ahmetalpbalkan Я согласен с Кэти, вы можете использовать его как ресурс для создания своей собственной таблицы поиска, логика должна быть довольно простой. К сожалению, версии для Java не существует.
Daniel Vandersluis
@ahmetalpbalkan Вот код для Java.
Якуб Джирутка 09
4

Нет простого или общего способа сделать то, что вы хотите, потому что это всего лишь ваше субъективное мнение, что эти буквы выглядят так, как латинские буквы, в которые вы хотите преобразовать. На самом деле это отдельные буквы со своими собственными именами и звуками, которые внешне выглядят как латинские буквы.

Если вы хотите этого преобразования, вы должны создать свою собственную таблицу перевода на основе того, в какие латинские буквы, по вашему мнению, следует преобразовывать нелатинские буквы.

(Если вы хотите удалить только диакритические знаки, в этой теме есть несколько ответов: Как удалить диакритические знаки (акценты) из строки в .NET? Однако вы описываете более общую проблему)

JacquesB
источник
+1. Вот Java-версия вопроса об удалении диакритических знаков: stackoverflow.com/questions/1016955/… ; см. ответы Майкла Боргвардта и Девио
Jonik
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
А еще ... Плохо.
cactuschibre
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 ЛАТИНСКАЯ СТРОЧНАЯ БУКВА S "ß" - это не та же буква Unicode, что и U + 03B2 ГРЕЧЕСКАЯ СТРОЧНАЯ БУКВА БЕТА "β".
Иоахим Зауэр,
2

Следующий класс делает свое дело:

org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter
TomWolk
источник