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
ł
不是附加符号。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;
}
}
核心的 java.text 包旨在解决这种情况(匹配字符串但不考虑变音符号、大小写等)。
配置一个 Collator
以按字符中的 PRIMARY
差异排序。然后为每个字符串创建一个 CollationKey
。如果您的所有代码都是 Java,可以直接使用 CollationKey
。如果需要将键存储在数据库或其他类型的索引中,可以将其转换为字节数组。
这些类使用 Unicode 标准大小写折叠数据来确定哪些字符是等效的,并支持各种分解策略。
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
类能够帮助您避免跟踪所有这些规则并使其保持最新状态。
a
的排序键转换为字节41、1、5、1、5、0,而字符串ab
转换为字节41、43、1、6、1、6、0。这些字节序列不会完整地出现在单词中(排序键a
的字节数组不会出现在排序键ab
的字节数组中)。 - Grzegorz Adam Hankiewiczbjo%
的前缀查询,您需要执行一个范围查询,其中排序器 >= bjo
且 < bjp
(或该语言环境中下一个符号是什么,没有编程方法来确定)。 - erickson您可以使用java.text
中的规范化类(Normalizer class):
System.out.println(new String(Normalizer.normalize("ń ǹ ň ñ ṅ ņ ṇ ṋ", Normalizer.Form.NFKD).getBytes("ascii"), "ascii"));
但是还有一些工作要做,因为Java对于无法转换的Unicode字符会产生奇怪的结果(它既不会忽略它们,也不会抛出异常)。不过我认为你可以以此作为起点。
在Unicode网站上有一份草案报告,内容与字符折叠有关。具体见第4.1节“折叠算法”:draft report。
这里提供一个使用Perl实现的消除变音符号的 讨论和实现。
以下是相关的现有SO问题:
对我来说,最简单的方法是维护一个稀疏映射数组,将Unicode代码点简单地转换为可显示的字符串。
例如:
start = 0x00C0
size = 23
mappings = {
"A","A","A","A","A","A","AE","C",
"E","E","E","E","I","I","I", "I",
"D","N","O","O","O","O","O"
}
start = 0x00D8
size = 6
mappings = {
"O","U","U","U","U","Y"
}
start = 0x00E0
size = 23
mappings = {
"a","a","a","a","a","a","ae","c",
"e","e","e","e","i","i","i", "i",
"d","n","o","o","o","o","o"
}
start = 0x00F8
size = 6
mappings = {
"o","u","u","u","u","y"
}
: : :
使用稀疏数组可以高效地表示替换,即使它们在Unicode表的广泛间隔部分中。字符串替换将允许任意序列替换您的变音符号(例如æ
字形变成ae
)。
这是一种与语言无关的答案,因此,如果您有特定的语言,请使用更好的方法(尽管它们最终都可能归结为这个方法)。