Gradle/Eclipse: Отличается ли поведение немецкого Umlaute при использовании равенства?

Я испытываю странное поведение с немецким "Umlaute" (ä, ö, ü, ß) при использовании проверок на равенство Java (прямо или косвенно). Все работает, как и ожидалось, при запуске, отладке или тестировании из Eclipse, и ввод, содержащий "Umlaute", является рассматривается как равный или не так, как ожидалось.

Однако, когда я создаю приложение с помощью Spring Boot и запускаю его, эти проверки на равенство не выполняются для слов, которые содержат "Umlaute", то есть для слов, таких как "Nationalität".

Входные данные извлекаются из веб-страницы с помощью Jsoup, а содержимое таблицы извлекается для некоторых ключевых слов. Кодировка страницы - UTF-8, и у меня есть обработка для Jsoup, чтобы преобразовать это, если это не так. Кодировка исходных файлов также UTF-8.

    Connection connection = Jsoup.connect(url)
                .header("accept-language", "de-de, de, en")
                .userAgent("Mozilla/5.0")
                .timeout(10000)
                .method(Method.GET);
    Response response = connection.execute();
    if(logger.isDebugEnabled())
        logger.debug("Encoding of response: " +response.charset());
    Document doc;
    if(response.charset().equalsIgnoreCase("UTF-8"))
    {
        logger.debug("Response has expected charset");
        doc = Jsoup.parse(response.body(), baseURL);
    }
    else
    {
        logger.debug("Response doesn't have exepcted charset and is converted");
        doc = Jsoup.parse(new String(response.bodyAsBytes(), "UTF-8"), baseURL);
    }

    logger.debug("Encoding of document: " +doc.charset());
    if(!doc.charset().equals(Charset.forName("UTF-8")))
    {
        logger.debug("Changing encoding of document from " +doc.charset());
        doc.updateMetaCharsetElement(true);
        doc.charset(Charset.forName("UTF-8"));
        logger.debug("Changed encoding of document to: " +doc.charset());
    }
    return doc;

Пример вывода журнала (из развернутого приложения) чтения содержимого.

Encoding of response: utf-8
Response has expected charset
Encoding of document: UTF-8

Пример ввода:

<tr><th>Nationalität:</th>     <td> [...] </td>    </tr>

Пример кода, который не подходит для слов, содержащих ä, ö, ü или ß, но отлично работает для других слов:

Element header = row.select("th").first();
String text = header.ownText();
if("Nationalität:".equals(text))
{
 // goes here in eclipse
}
else
{
 // and here in deployed spring boot app
}

Есть ли какая-то разница между запуском из Eclipse и встроенным и развернутым приложением, которое мне не хватает? Откуда еще может появиться это поведение и как мне это решить?

Насколько я понимаю, это не является (напрямую) проблемой кодирования, поскольку входные данные правильно отображают "Umlaute"... Поскольку это невозможно воспроизвести при отладке, мне трудно понять, что именно идет не так.

Изменить: хотя вход выглядит хорошо в журналах (т.е. диакритические знаки отображаются правильно), я понял, что они не выглядят правильно в консоли:<th>Nationalit├ñt:</th>

В настоящее время я использую нормализатор, предложенный Мирко, вот так:Normalizer.normalize(input, Form.NFC);(также попробовал это с NFD). Чем отличаются (SpringBoot-) консоль и (logback) выход из системы?

3 ответа

Решение

Я думаю, что отследил это до сборки автономного приложения, являющегося виновником. Как описано выше, при запуске из Eclipse все в порядке, проблема возникала только при запуске автономного приложения Spring Boot.

Это строится с Gradle. У меня в build.gradle есть

compileJava.options.encoding = 'UTF-8'

чтобы заставить UTF-8 использоваться для кодирования. Этого должно быть (обычно) достаточно. Однако я также использую AspectJ (через плагин gradle-aspectj), который явно нарушает это поведение (невольно?) И приводит к кодировке по умолчанию, которая будет использоваться вместо явно заданной. Для решения этой проблемы я добавил

compileAspect {
  additionalAjcArgs = ['encoding' : 'UTF-8']
}

в мой build.gradle, который передает параметр кодирования в компилятор ajc. Это, похоже, решило проблему для обычной сборки.

Однако проблема все еще возникает, когда тесты запускаются из Gradle. Я еще не смог выяснить, что нужно делать там и почему вышеуказанной конфигурации недостаточно. Теперь это отслеживается в отдельном вопросе.

Диакритические знаки, такие как умлауты, часто могут быть представлены двумя разными способами в юникоде: как символ с одной кодовой точкой или как композиция из двух символов. Это не проблема кодирования, это может происходить в UTF-8, UTF-16, UTF-32 и т. Д. Метод равенства Java может не считать составные символы равными символам с одной кодовой точкой, даже если они выглядят абсолютно одинаково. Попробуйте взглянуть на двоичное представление сравниваемых строк, чтобы вы могли отследить различия. Вы также можете использовать методы класса "Character", чтобы перебирать строки и распечатывать свойства всех символов. Может быть, это тоже помогает выяснить различия.

В любом случае, это может помочь, если вы используете java.text.Normalizer по обеим сторонам от "равно", чтобы нормализовать текст, например, к форме нормализации Unicode C. Таким образом, различия, подобные вышеупомянутым, должны быть исправлены, и строки должны сравниваться, как и ожидалось.

Вы пытались распечатать код ключа на консоли, чтобы увидеть, действительно ли они совпадают при компиляции? Может быть, Eclipse изящно обрабатывает кодировку, но когда она скомпилирована, все зависит от настроек Java/System?

Другие вопросы по тегам