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

Remove diacritical marks (ń ǹ ň ñ ṅ ņ ṇ ṋ ṉ ̈ ɲ ƞ ᶇ ɳ ȵ) from Unicode chars

Удаление диакритических знаков из символов Юникода

Я рассматриваю алгоритм, который может сопоставлять символы с диакритическими знаками (тильда, окружность, каретка, умлаут, карон) и их "простым" символом.

Например:

ń  ǹ  ň  ñ  ṅ  ņ  ṇ  ṋ  ṉ  ̈  ɲ  ƞ ᶇ ɳ ȵ  --> n
á --> a
ä --> a
ấ --> a
ṏ --> o

И др.


  1. Я хочу сделать это на Java, хотя подозреваю, что это должно быть что-то на языке Unicode и должно быть достаточно легко выполнимо на любом языке.


  2. Цель: упростить поиск слов с диакритическими знаками. Например, если у меня есть база данных теннисистов, и в ней введен Björn_Borg, я также сохраню Bjorn_Borg, чтобы я мог найти его, если кто-то введет Bjorn, а не Björn.


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

Недавно я сделал это на Java:

public static final Pattern DIACRITICS_AND_FRIENDS
= Pattern.compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}\\u0591-\\u05C7]+");

private static String stripDiacritics(String str) {
str = Normalizer.normalize(str, Normalizer.Form.NFD);
str = DIACRITICS_AND_FRIENDS.matcher(str).replaceAll("");
return str;
}

Это будет сделано так, как вы указали:

stripDiacritics("Björn")  = Bjorn

но это не удастся, например, в Białystok, потому что ł символ не является диакритическим.

Если вы хотите иметь полноценный упрощитель строк, вам потребуется второй раунд очистки для еще нескольких специальных символов, которые не являются диакритическими знаками. В эту карту я включил наиболее распространенные специальные символы, которые появляются в именах наших клиентов. Это не полный список, но он даст вам представление о том, как его расширить. ImmutableMap - это всего лишь простой класс из Google-коллекций.

public class StringSimplifier {
public static final char DEFAULT_REPLACE_CHAR = '-';
public static final String DEFAULT_REPLACE = String.valueOf(DEFAULT_REPLACE_CHAR);
private static final ImmutableMap<String, String> NONDIACRITICS = ImmutableMap.<String, String>builder()

//Remove crap strings with no sematics
.put(".", "")
.put("\"", "")
.put("'", "")

//Keep relevant characters as seperation
.put(" ", DEFAULT_REPLACE)
.put("]", DEFAULT_REPLACE)
.put("[", DEFAULT_REPLACE)
.put(")", DEFAULT_REPLACE)
.put("(", DEFAULT_REPLACE)
.put("=", DEFAULT_REPLACE)
.put("!", DEFAULT_REPLACE)
.put("/", DEFAULT_REPLACE)
.put("\\", DEFAULT_REPLACE)
.put("&", DEFAULT_REPLACE)
.put(",", DEFAULT_REPLACE)
.put("?", DEFAULT_REPLACE)
.put("°", DEFAULT_REPLACE) //Remove ?? is diacritic?
.put("|", DEFAULT_REPLACE)
.put("<", DEFAULT_REPLACE)
.put(">", DEFAULT_REPLACE)
.put(";", DEFAULT_REPLACE)
.put(":", DEFAULT_REPLACE)
.put("_", DEFAULT_REPLACE)
.put("#", DEFAULT_REPLACE)
.put("~", DEFAULT_REPLACE)
.put("+", DEFAULT_REPLACE)
.put("*", DEFAULT_REPLACE)

//Replace non-diacritics as their equivalent characters
.put("\u0141", "l") // BiaLystock
.put("\u0142", "l") // Bialystock
.put("ß", "ss")
.put("æ", "ae")
.put("ø", "o")
.put("©", "c")
.put("\u00D0", "d") // All Ð ð from http://de.wikipedia.org/wiki/%C3%90
.put("\u00F0", "d")
.put("\u0110", "d")
.put("\u0111", "d")
.put("\u0189", "d")
.put("\u0256", "d")
.put("\u00DE", "th") // thorn Þ
.put("\u00FE", "th") // thorn þ
.build();


public static String simplifiedString(String orig) {
String str = orig;
if (str == null) {
return null;
}
str = stripDiacritics(str);
str = stripNonDiacritics(str);
if (str.length() == 0) {
// Ugly special case to work around non-existing empty strings
// in Oracle. Store original crapstring as simplified.
// It would return an empty string if Oracle could store it.
return orig;
}
return str.toLowerCase();
}

private static String stripNonDiacritics(String orig) {
StringBuilder ret = new StringBuilder
String lastchar = null;
for (int i = 0; i < orig.length(); i++) {
String source = orig.substring(i, i + 1);
String replace = NONDIACRITICS.get(source);
String toReplace = replace == null ? String.valueOf(source) : replace;
if (DEFAULT_REPLACE.equals(lastchar) && DEFAULT_REPLACE.equals(toReplace)) {
toReplace = "";
} else {
lastchar = toReplace;
}
ret.append(toReplace);
}
if (ret.length() > 0 && DEFAULT_REPLACE_CHAR == ret.charAt(ret.length() - 1)) {
ret.deleteCharAt(ret.length() - 1);
}
return ret.toString();
}

/*
Special regular expression character ranges relevant for simplification:
- InCombiningDiacriticalMarks: diacritic marks used in many languages
- IsLm: Letter, Modifier (see http://www.fileformat.info/info/unicode/category/Lm/list.htm)
- IsSk: Symbol, Modifier (see http://www.fileformat.info/info/unicode/category/Sk/list.htm)
- U+0591 to U+05C7: Range for Hebrew diacritics (niqqud)
(see official Unicode chart: https://www.unicode.org/charts/PDF/U0590.pdf)
*/

public static final Pattern DIACRITICS_AND_FRIENDS = Pattern.compile(
"[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}\\u0591-\\u05C7]+"
);


private static String stripDiacritics(String str) {
str = Normalizer.normalize(str, Normalizer.Form.NFD);
str = DIACRITICS_AND_FRIENDS.matcher(str).replaceAll("");
return str;
}
}
Ответ 2

Основной пакет java.text был разработан для решения этого варианта использования (сопоставление строк без учета диакритических знаков, регистра и т.д.).

Настройте Collator для сортировки по PRIMARY различиям в символах. После этого создайте CollationKey для каждой строки. Если весь ваш код написан на Java, вы можете использовать CollationKey напрямую. Если вам нужно сохранить ключи в базе данных или другом виде индекса, вы можете преобразовать его в массив байтов.

Эти классы используют данные о сложении регистра в стандарте Юникода, чтобы определить, какие символы эквивалентны, и поддерживают различные стратегии декомпозиции.

Collator c = Collator.getInstance();
c.setStrength(Collator.PRIMARY);
Map<CollationKey, String> dictionary = new TreeMap<CollationKey, String>();
dictionary.put(c.getCollationKey("Björn"), "Björn");
...
CollationKey query = c.getCollationKey("bjorn");
System.out.println(dictionary.get(query)); // --> "Björn"

Обратите внимание, что средства сортировки зависят от локали. Это связано с тем, что "алфавитный порядок" отличается в разных локалях (и даже с течением времени, как это было в случае с испанским). Класс Collator избавляет вас от необходимости отслеживать все эти правила и поддерживать их в актуальном состоянии.

Ответ 3

Это часть языка Apache Commons начиная с версии 3.1.

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

ВОЗВРАТ An

Ответ 4

Вы могли бы использовать класс нормализатора из java.text:

System.out.println(new String(Normalizer.normalize("ń ǹ ň ñ ṅ ņ ṇ ṋ", Normalizer.Form.NFKD).getBytes("ascii"), "ascii"));

Но еще предстоит проделать некоторую работу, поскольку Java создает странные вещи с неконвертируемыми символами Юникода (она не игнорирует их и не создает исключение). Но я думаю, вы могли бы использовать это в качестве отправной точки.

2023-09-27 08:31 java