我正在寻找一种算法,该算法可以在带有变音符号的字符(tilde,circumflex,caret,umlaut,caron)与其“简单”字符之间进行映射。
例如:
ń ǹ ň ñ ṅ ņ ṇ ṋ ṉ ̈ ɲ ƞ ᶇ ɳ ȵ --> n á --> a ä --> a ấ --> a ṏ --> o
等等。
我想用Java做到这一点,尽管我怀疑它应该是Unicode-y,并且应该可以轻松地以任何语言进行操作。
目的:允许轻松搜索带有变音标记的单词。例如,如果我有一个网球运动员数据库,并且输入了Björn_Borg,我还将保留Bjorn_Borg,以便在有人进入Bjorn而不是Björn的情况下可以找到它。
我最近在Java中完成了此操作:
public static final Pattern DIACRITICS_AND_FRIENDS = Pattern.compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+"); 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-collections中的一个简单类。
immutableMap
google-collections
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) { StringBuffer ret = new StringBuffer(); 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 -> see http://docstore.mik.ua/orelly/perl/prog3/ch05_04.htm InCombiningDiacriticalMarks: special marks that are part of "normal" ä, ö, î etc.. IsSk: Symbol, Modifier see http://www.fileformat.info/info/unicode/category/Sk/list.htm IsLm: Letter, Modifier see http://www.fileformat.info/info/unicode/category/Lm/list.htm */ public static final Pattern DIACRITICS_AND_FRIENDS = Pattern.compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+"); private static String stripDiacritics(String str) { str = Normalizer.normalize(str, Normalizer.Form.NFD); str = DIACRITICS_AND_FRIENDS.matcher(str).replaceAll(""); return str; } }