【问题标题】:How I can use InCombiningDiacriticalMarks ignoring one case我如何使用 InCombiningDiacriticalMarks 忽略一个案例
【发布时间】:2020-06-30 00:41:54
【问题描述】:

我正在编写删除一个字符串的所有变音符号的代码。

例如:áÁéÉíÍóÓúÚäÄëËïÏöÖüÜñÑ

我正在使用 Unicode 的属性InCombiningDiacriticalMarks。但我想忽略 ñÑ 的替换。

现在我保存这两个字符,然后替换为:

    s = s.replace('ñ', '\001');
    s = s.replace('Ñ', '\002');

可以使用InCombiningDiacriticalMarks 忽略ñÑ 的变音符号。

这是我的代码:

public static String stripAccents(String s) 
{
    /*Save ñ*/
    s = s.replace('ñ', '\001');
    s = s.replace('Ñ', '\002');
    s = Normalizer.normalize(s, Normalizer.Form.NFD);
    s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
    /*Add ñ to s*/
    s = s.replace('\001', 'ñ');
    s = s.replace('\002', 'Ñ');

    return s;
}   

它工作正常,但我想知道是否可以优化此代码。

【问题讨论】:

    标签: java regex unicode


    【解决方案1】:

    这取决于您所说的“优化”是什么意思。很难从您编写的代码中减少代码行数,但是由于您要处理字符串六次,因此可以通过只处理输入字符串一次,逐个字符来提高性能:

    public class App {
    
        // See SO answer https://stackoverflow.com/a/10831704/2985643 by virgo47
        private static final String tab00c0
                = "AAAAAAACEEEEIIII"
                + "DNOOOOO\u00d7\u00d8UUUUYI\u00df"
                + "aaaaaaaceeeeiiii"
                + "\u00f0nooooo\u00f7\u00f8uuuuy\u00fey"
                + "AaAaAaCcCcCcCcDd"
                + "DdEeEeEeEeEeGgGg"
                + "GgGgHhHhIiIiIiIi"
                + "IiJjJjKkkLlLlLlL"
                + "lLlNnNnNnnNnOoOo"
                + "OoOoRrRrRrSsSsSs"
                + "SsTtTtTtUuUuUuUu"
                + "UuUuWwYyYZzZzZzF";
    
        public static void main(String[] args) {
            var input = "AaBbCcáÁéÉíÍóÓúÚäÄëËïÏöÖüÜñÑçÇ";
            var output = removeDiacritic(input);
            System.out.println("input  = " + input);
            System.out.println("output = " + output);
        }
    
        public static String removeDiacritic(String input) {
            var output = new StringBuilder(input.length());
            for (var c : input.toCharArray()) {
                if (isModifiable(c)) {
                    c = tab00c0.charAt(c - '\u00c0');
                }
                output.append(c);
            }
            return output.toString();
        }
    
        // Returns true if the supplied char is a candidate for diacritic removal. 
        static boolean isModifiable(char c) {
            boolean modifiable;
    
            if (c < '\u00c0' || c > '\u017f') {
                modifiable = false;
            } else {
                modifiable = switch (c) {
    
                    case 'ñ', 'Ñ' ->
                        false;
                    default ->
                        true;
                };
            }
            return modifiable;
        }
    }
    

    这是运行代码的输出:

    input  = AaBbCcáÁéÉíÍóÓúÚäÄëËïÏöÖüÜñÑçÇ
    output = AaBbCcaAeEiIoOuUaAeEiIoOuUñÑcC
    

    输入字符串中没有变音符号的字符不会被修改。否则,除ñÑ 的情况外,将删除变音符号(例如ÇC)。

    注意事项:

    • 代码根本不使用Normalizer 类或InCombiningDiacriticalMarks。相反,它只处理输入字符串中的每个字符一次,并在适当的情况下删除其重音。据我所知,删除变音符号的传统方法(如 OP 中所用)不支持选择性删除。
    • 代码基于on an answer by user virgo47,但经过增强以支持选择性删除重音。有关将重音字符映射到非重音字符的详细信息,请参阅 virgo47 的答案。
    • 此解决方案仅适用于 Latin-1/Latin-2,但可以增强以支持其他映射。
    • 您的解决方案非常简短且易于理解,但感觉很脆弱,而且对于大量输入,我怀疑它会比只处理每个字符一次的方法慢得多。

    【讨论】:

    • 您不需要isModifiable 方法。最好更改 tab00c0 字符串以保留 'Ñ' 和 'ñ' 字符:private static final String tab00c0 = "AAAAAAACEEEEIIIIDÑOOOOO\u00d7\u00d8UUUUYI\u00dfaaaaaaaceeeeiiii\u00f0ñooooo\u00f7\u00f8uuuuy\u00feyAaAaAaCcCcCcCcDdDdEeEeEeEeEeGgGgGgGgHhHhIiIiIiIiIiJjJjKkkLlLlLlLlLlNnNnNnnNnOoOoOoOoRrRrRrSsSsSsSsTtTtTtUuUuUuUuUuUuWwYyYZzZzZzF";
    • @Charliemops 我认为您应该发布答案。如果它改进了 OP 的代码,我会很乐意投票。
    猜你喜欢
    • 2018-05-19
    • 1970-01-01
    • 2011-06-05
    • 2015-10-09
    • 1970-01-01
    • 1970-01-01
    • 2017-10-25
    • 1970-01-01
    • 2012-03-27
    相关资源
    最近更新 更多