从Unicode字符中删除变音符号(ńǹṅṅņṇṋ̈ɲƞᶇɳȵ)

fly*_*ire 85 java unicode diacritics transliteration

我在看的算法,可以用变音符号(字符之间映射波浪,抑扬,插入符号,变音符号,卡隆)和他们的"简单"的角色.

例如:

?  ?  ?  ñ  ?  ?  ?  ?  ?  ?  ?  ? ? ? ?  --> n
á --> a
ä --> a
? --> a
? --> o
Run Code Online (Sandbox Code Playgroud)

等等.

  1. 我想用Java做这个,虽然我怀疑它应该是Unicode-y,并且应该可以在任何语言中合理地使用.

  2. 目的:允许轻松搜索带有变音符号的单词.例如,如果我有一个网球运动员数据库,并且输入了Björn_Borg,我还会保留Bjorn_Borg,这样如果有人进入Bjorn而不是Björn,我就能找到它.

And*_*son 77

我最近用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;
}
Run Code Online (Sandbox Code Playgroud)

这将按照您的指定执行:

stripDiacritics("Björn")  = Bjorn
Run Code Online (Sandbox Code Playgroud)

但它会失败,例如比亚韦斯托克,因为这个?角色不是变音符号.

如果你想拥有一个完整的字符串简化器,你需要进行第二轮清理,以获得一些不是变音符号的特殊字符.这张地图,我已经包含了我们客户名称中出现的最常见的特殊字符.它不是一个完整的列表,但它会让你知道如何扩展它.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢安德烈亚斯,工作就像一个魅力!(在rrrr̈r'ŕřttẗţỳỹẙy'yýÿŷpp̈sss̈s̊s's̸śŝŞşšddd̈ďd'ḑf̈f̸ggg̈g'ģqĝǧḧĥj̈j'ḱkk̈k̸ǩlll̈Łłẅẍcc̈c̊c'c̸Çççĉĉčvv̈v'v̸bb̧ǹnn̈n̊n'ńņňmmmm̈m̊m̌ǵß上测试):-) (3认同)
  • 正如我在问题评论中所说.如果你想要一个好的搜索引擎,你不能依赖基本的变音标记删除. (2认同)

eri*_*son 23

核心java.text包旨在解决这个用例(匹配字符串而不关心变音符号,大小写等).

配置a 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"
Run Code Online (Sandbox Code Playgroud)

请注意,collat​​ors是特定于语言环境的.这是因为"字母顺序"在区域设置之间是不同的(甚至随着时间的推移,与西班牙语的情况一样).该Collator课程使您无需跟踪所有这些规则并使其保持最新状态.


Ken*_*hoi 15

它是Apache Commons Lang的一部分.3.1.

org.apache.commons.lang3.StringUtils.stripAccents("Añ");
Run Code Online (Sandbox Code Playgroud)

回报 An

  • 谢谢迈克指出这一点.该方法仅处理重音."ǹǹṅņṇṇṋṉɲƞƞᶇȵȵ"的结果是"nnnnnnnnɲƞᶇɳȵ" (2认同)

nil*_*ils 11

您可以使用正规化类java.text:

System.out.println(new String(Normalizer.normalize("? ? ? ñ ? ? ? ?", Normalizer.Form.NFKD).getBytes("ascii"), "ascii"));
Run Code Online (Sandbox Code Playgroud)

但仍有一些工作要做,因为Java使用不可转换的Unicode字符做出奇怪的事情(它不会忽略它们,并且它不会抛出异常).但我认为你可以用它作为起点.

  • 这对于非ascii变音符号不起作用,例如在俄语中,它们也有变音符号,而且还有所有亚洲字符串.不使用.代替转换为ascii,使用\\ p {InCombiningDiacriticalMarks}正则表达式中的答案http://stackoverflow.com/questions/1453171/n-nto-n-or-remove-diacritical-marks-from/1453284#1453284 (2认同)

ire*_*ses 10

有一个报告草案上字符的Unicode网站里面有很多相关的材料对折.具体见4.1节."折叠算法".

这是使用Perl去除变音标记的讨论和实现.

这些现有的SO问题是相关的:


unw*_*ind 5

请注意,并非所有这些标记都只是某些“正常”字符上的“标记”,您可以在不改变含义的情况下将其删除。

在瑞典语中,å ä 和 ö 是真实且恰当的一流字符,而不是其他字符的“变体”。它们听起来与所有其他字符不同,它们的排序不同,并且它们使单词改变含义(“mätt”和“matt”是两个不同的词)。

  • 虽然正确,但这与其说是对问题的回答,不如说是评论。 (4认同)