Вопрос-ответ

Is there a way to get rid of accents and convert a whole string to regular letters?

Есть ли способ избавиться от акцентов и преобразовать всю строку в обычные буквы?

Есть ли лучший способ избавиться от акцентов и сделать эти буквы обычными, кроме использования String.replaceAll() метода и замены букв одну за другой? Пример:

Ввод: orčpžsíáýd

Вывод: orcpzsiayd

Необязательно включать все буквы с акцентами, как в русском алфавите или китайском.

Переведено автоматически
Ответ 1

Используйте java.text.Normalizer, чтобы справиться с этим за вас.

string = Normalizer.normalize(string, Normalizer.Form.NFD);
// or Normalizer.Form.NFKD for a more "compatible" deconstruction

Это отделит все знаки ударения от символов. Затем вам просто нужно сравнить каждый символ с буквой и исключить те, которые таковыми не являются.

string = string.replaceAll("[^\\p{ASCII}]", "");

Если ваш текст в юникоде, вам следует использовать это вместо:

string = string.replaceAll("\\p{M}", "");

Для unicode, \\P{M} соответствует базовому глифу и \\p{M} (в нижнем регистре) соответствует каждому акценту.

Спасибо GarretWilson за указатель и regular-expressions.info за отличное руководство по юникоду.

Ответ 2

С 2011 года вы можете использовать Apache Commons StringUtils.stripAccents (ввод) (начиная с версии 3.0):

    String input = StringUtils.stripAccents("Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ");
System.out.println(input);
// Prints "This is a funky String"

Примечание:

Принятый ответ (Эрика Робертсона) не работает для Ø или L . Apache Commons 3.5 также не работает для Ø, но он работает для L. После прочтения статьи в Википедии о, я не уверен, что ее следует заменить на "O": это отдельная буква в норвежском и датском языках, расположенная в алфавитном порядке после "z". Это хороший пример ограничений подхода "stripe accents".

Ответ 3

Решение от @virgo47 очень быстрое, но приблизительное. Принятый ответ использует нормализатор и регулярное выражение. Мне было интересно, какую часть времени занял нормализатор по сравнению с регулярным выражением, поскольку удаление всех символов, отличных от ASCII, можно выполнить без регулярного выражения:

import java.text.Normalizer;

public class Strip {
public static String flattenToAscii(String string) {
StringBuilder sb = new StringBuilder(string.length());
string = Normalizer.normalize(string, Normalizer.Form.NFD);
for (char c : string.toCharArray()) {
if (c <= '\u007F') sb.append(c);
}
return sb.toString();
}
}

Небольшое дополнительное ускорение можно получить, записав в символ[] и не вызывая toCharArray(), хотя я не уверен, что снижение четкости кода того заслуживает:

public static String flattenToAscii(String string) {
char[] out = new char[string.length()];
string = Normalizer.normalize(string, Normalizer.Form.NFD);
int j = 0;
for (int i = 0, n = string.length(); i < n; ++i) {
char c = string.charAt(i);
if (c <= '\u007F') out[j++] = c;
}
return new String(out);
}

Преимущество этого варианта заключается в корректности варианта, использующего нормализатор, и некоторой скорости варианта, использующего таблицу. На моей машине этот ответ примерно в 4 раза быстрее принятого ответа и в 6,6-7 раз медленнее, чем у @virgo47 (принятый ответ примерно в 26 раз медленнее, чем у @virgo47 на моей машине).

Ответ 4

РЕДАКТИРОВАТЬ: Если вы не привязаны к Java <6, скорость не критична и / или таблица перевода слишком ограничена, используйте ответ Дэвида. Смысл в том, чтобы использовать Normalizer (введено в Java 6) вместо таблицы перевода внутри цикла.

Хотя это и не "идеальное" решение, оно хорошо работает, когда вы знаете диапазон (в нашем случае Latin1, 2), работало до Java 6 (хотя это и не настоящая проблема) и намного быстрее, чем наиболее рекомендуемая версия (может быть, проблема, а может и не быть):

    /**
* Mirror of the unicode table from 00c0 to 017f without diacritics.
*/

private static final String tab00c0 = "AAAAAAACEEEEIIII" +
"DNOOOOO\u00d7\u00d8UUUUYI\u00df" +
"aaaaaaaceeeeiiii" +
"\u00f0nooooo\u00f7\u00f8uuuuy\u00fey" +
"AaAaAaCcCcCcCcDd" +
"DdEeEeEeEeEeGgGg" +
"GgGgHhHhIiIiIiIi" +
"IiJjJjKkkLlLlLlL" +
"lLlNnNnNnnNnOoOo" +
"OoOoRrRrRrSsSsSs" +
"SsTtTtTtUuUuUuUu" +
"UuUuWwYyYZzZzZzF";

/**
* Returns string without diacritics - 7 bit approximation.
*
* @param source string to convert
* @return corresponding string without diacritics
*/

public static String removeDiacritic(String source) {
char[] vysl = new char[source.length()];
char one;
for (int i = 0; i < source.length(); i++) {
one = source.charAt(i);
if (one >= '\u00c0' && one <= '\u017f') {
one = tab00c0.charAt((int) one - '\u00c0');
}
vysl[i] = one;
}
return new String(vysl);
}

Тесты на моем HW с 32-битным JDK показывают, что это выполняет преобразование из aeelstc89FDC в aeelstc89FDC 1 миллион раз за ~ 100 мс, в то время как Normalizer way делает это за 3,7 с (в 37 раз медленнее). Если вам нужна производительность и вы знаете диапазон ввода, это может быть для вас.

Наслаждайтесь :-)

java string