java.text.Collat​​or将“ v”和“ w”视为瑞典语语言/语言环境的相同字母

时间:2019-02-20 15:03:54

标签: java java-8

以下测试在Java 8中正确通过。

Comparator<String> stringComparator = Collator.getInstance(new Locale("sv", "SE"));

Assert.assertTrue(stringComparator.compare("aaaa", "bbbb") < 0);
Assert.assertTrue(stringComparator.compare("waaa", "vbbb") < 0);
Assert.assertTrue(stringComparator.compare("vaaa", "wbbb") < 0);

此命令在waaa之前将vbbbvaaa之前的wbbb排序。显然,它将vw视为同一字母。

实际上,根据Wikipedia所说,它是瑞典语:

  

到2006年,由于使用了新的外来词,“ W”的使用量有所增加,因此“ W”正式成为字母,并且不赞成使用“ V” =“ W”的排序规则。 2006年前的书籍和软件通常使用该规则。弃用该规则后,一些书籍和软件继续应用该规则。

有人对此有一个通用的解决方法,以便在瑞典语言环境中将vw视为单独的字母吗?

3 个答案:

答案 0 :(得分:4)

创建自己的RuleBasedCollator

检查返回的字符串的值

((RuleBasedCollator)Collator.getInstance(new Locale("sv", "SE"))).getRules()

并对其进行修改以适合您的需求,然后使用修改后的规则创建一个新的整理器。

也许也可以提交一个JDK错误报告,以作为一种很好的措施。

答案 1 :(得分:1)

  

此命令在vbbb之前命令waaa,在wbbb之前命令vaaa。显然是   将v和w视为相同的字母。

即使在瑞典语区域设置中,JDK确实也不会将'w'和'v'视为相同的字符。字母“ v”在“ w”之前。

Assert.assertEquals(1, stringComparator.compare("w", "v"));//TRUE

但是,基于瑞典的整理规则,JDK在“ vb”之前命令“ wa”。

Assert.assertEquals(1, stringComparator.compare("wa", "vb"));//FALSE

答案 2 :(得分:1)

您可以创建一个自定义比较器,该包装器将包装整理器并按您想要的方式手动处理vw

我已经实现了两个实现。

第一个简短而优雅,它使用Guavas lexicographical比较器以及Holger在评论中提供的棘手正则表达式。

private static final Pattern VW_BOUNDARY = Pattern.compile("(?=[vw])|(?<=[vw])", Pattern.CASE_INSENSITIVE);

public static Comparator<String> smallCorrectVwWrapper(Comparator<Object> original) {
    return Comparator.comparing(
        s -> Arrays.asList(VW_BOUNDARY.split((String) s)),
        Comparators.lexicographical(original));

第二种实现是一件又大又复杂的事情,它做同样的事情,但是是手动实现的,没有库和正则表达式。

public static Comparator<String> correctVwWrapper(Comparator<Object> original) {
    return (s1, s2) -> compareSplittedVw(original, s1, s2);
}

/**
 * Compares the two string by first splitting them into segments separated by W
 * and V, then comparing the segments one by one.
 */
private static int compareSplittedVw(Comparator<Object> original, String s1, String s2) {
    List<String> l1 = splitVw(s1);
    List<String> l2 = splitVw(s2);

    int minSize = Math.min(l1.size(), l2.size());

    for (int ix = 0; ix < minSize; ix++) {
        int comp = original.compare(l1.get(ix), l2.get(ix));
        if (comp != 0) {
            return comp; 
        }
    }

    return Integer.compare(l1.size(), l2.size());
}

private static boolean isVw(int ch) {
    return ch == 'V' || ch == 'v' || ch == 'W' || ch == 'w';
}


/**
 * Splits the string into segments separated by V and W.
 */
public static List<String> splitVw(String s) {
    var b = new StringBuilder();

    var result = new ArrayList<String>();

    for (int offset = 0; offset < s.length();) {
        int ch = s.codePointAt(offset);

        if (isVw(ch)) {
            if (b.length() > 0) {
                result.add(b.toString());
                b.setLength(0);
            }

            result.add(Character.toString((char) ch));
        } else {
            b.appendCodePoint(ch);
        }

        offset += Character.charCount(ch);
    }

    if (b.length() > 0) {
        result.add(b.toString());
    }

    return result;
}

用法:

public static void main(String[] args) throws Exception {
    Comparator<String> stringComparator = correctVwWrapper(Collator.getInstance(new Locale("sv", "SE")));

    System.out.println(stringComparator.compare("a", "z") < 0);     // true
    System.out.println(stringComparator.compare("wa", "vz") < 0);   // false
    System.out.println(stringComparator.compare("wwa", "vvz") < 0); // false
    System.out.println(stringComparator.compare("va", "wz") < 0);   // true
    System.out.println(stringComparator.compare("v", "w") < 0);     // true
}

要实现包装Collator,还需要做更多的工作,但是应该不会太复杂。