java - 如何使用 InCombiningDiariticMarks 忽略一种情况

标签 java regex unicode

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

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

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

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

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

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

这是我的代码:

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;
}   

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

最佳答案

这取决于您所说的“优化”的含义。减少所编写的代码行数很困难,但由于您要处理字符串六次,因此可以通过仅逐个字符地处理输入字符串一次来提高性能:

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

关于java - 如何使用 InCombiningDiariticMarks 忽略一种情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60747707/

相关文章:

java - 为什么要以静态方式访问静态字段?

regex - Grep歧义嵌套方括号

regex - 删除文本文件中从第一个空行开始的所有内容

c++ - en_US.UTF-8 语言环境的 Windows 等效项是什么?

vba - 从 vba 到平面文件的 Unicode 字符串

java - 为什么我的 formdata.append 没有将键值对发送到服务器?

java - 没有为操作定义结果。验证 xml 上的表单内容时

java - 获取特定字符串后的所有内容

jquery - 在文本框中动态添加 Html 复选标记字符

java - 带有自定义注释的抽象类