java.text.Collator обрабатывает "v" и "w" как одну и ту же букву для шведского языка / локали - PullRequest
0 голосов
/ 20 февраля 2019

Следующий тест проходит правильно с 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 до vbbb и vaaa до wbbb.Очевидно, он обрабатывает v и w как одну и ту же букву.

На самом деле, согласно Википедии, на шведском языке:

К 2006 году использование W расширилось из-за новых заимствований, поэтому «W» официально стало буквой,и правило сортировки 'V' = 'W' устарело.Книги и программное обеспечение до 2006 года обычно используют это правило.После того, как правило устарело, некоторые книги и программное обеспечение продолжали применять его.

Есть ли у кого-нибудь общий обходной путь, чтобы v и w рассматривались как отдельные буквы в шведском языке?

Ответы [ 3 ]

0 голосов
/ 20 февраля 2019

Это заказывает waaa перед vbbb и vaaa перед wbbb.Очевидно, он рассматривает v и w как одну и ту же букву.

JDK действительно не рассматривает 'w' и 'v' как одни и те же символы, даже в шведской локали.Буква «v» стоит перед «w».

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

Однако, исходя из шведских правил сопоставления, JDK приказывает 'wa' опережать 'vb'.

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

0 голосов
/ 20 февраля 2019

Вы можете создать пользовательский компаратор, который оборачивает разборщик и обрабатывает вручную v и w так, как вы хотите.

Я сделал две реализации этого.

Первый из них короткий и элегантный, он использует компаратор Guavas lexicographical вместе с хитрым регулярным выражением, которое Хольгер представил в комментарии.

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, но это не должно быть слишком сложно.

0 голосов
/ 20 февраля 2019

Создайте свой собственный RuleBasedCollator .

Проверьте значение строки, возвращаемой

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

, и измените ее в соответствии с вашими потребностями, а затем создайте новый сборщикс вашими модифицированными правилами.

И, возможно, для большей степени отправьте отчет об ошибке JDK.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...