API или метод для замены всех нелатинских символов

Я имею дело со сторонними API / Web-сервисами, и они разрешают только латинский-1 набор символов в своем XML. Существует ли существующий API / метод, который найдет и заменит все символы нелатинского языка-1 в строке?

Например: Кевин

Есть ли способ сделать этого Кевина?

2 ответа

Решение

Используя ICU4J,

public String removeAccents(String text) {
    return Normalizer.decompose(text, false, 0)
                 .replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}

Я нашел этот пример на http://glaforge.appspot.com/article/how-to-remove-accents-from-a-string

В Java 1.6 необходимый нормализатор может быть встроен.

Я наткнулся на множество сообщений о том, как удалить все акценты. Этот (старый!) пост описывает мой вариант использования, поэтому я поделюсь здесь своим решением. В моем случае я хочу заменить только символы, отсутствующие в кодировке ISO-8859-1. Вариант использования: прочитать файл UTF-8 и записать его в файл ISO-8859-1, сохранив при этом как можно больше специальных символов (но предотвратив UnmappableCharacterException).

      @Test
void proofOfConcept() {
    final String input = "Bełchatöw";
    final String expected = "Belchatöw";
    final String actual = MyMapper.utf8ToLatin1(input);
    Assertions.assertEquals(expected, actual);
}

Нормализатор кажется интересным, но я нашел только способы убрать все акценты.

      public static String utf8ToLatin1(final String input) {
    return Normalizer.normalize(input, Normalizer.Form.NFD)
        .replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}

Как ни странно, приведенный выше код не только не работает, но и с

      expected: <Belchatöw> but was: <Bełchatow>

CharsetEncoder кажется интересным, но, похоже, я могу установить только статический символ «замены» (на самом деле: массив байтов), поэтому все неотображаемые символы становятся '?' или похожие

      public static String utf8ToLatin1(final String input) throws CharacterCodingException {
    final ByteBuffer byteBuffer = StandardCharsets.ISO_8859_1.newEncoder()
        .onMalformedInput(CodingErrorAction.REPLACE)
        .onUnmappableCharacter(CodingErrorAction.REPLACE)
        .replaceWith(new byte[] { (byte) '?' })
        .encode(CharBuffer.wrap(input));
    return new String(byteBuffer.array(), StandardCharsets.ISO_8859_1);
}

Сбой с

      expected: <Belchatöw> but was: <Be?chatöw>

Мое окончательное решение таково:

      public static String utf8ToLatin1(final String input) {
    final Map<String, String> characterMap = new HashMap<>();
    characterMap.put("ł", "l");
    characterMap.put("Ł", "L");
    characterMap.put("œ", "ö");
    final StringBuffer resultBuffer = new StringBuffer();
    final Matcher matcher = Pattern.compile("[^\\p{InBasic_Latin}\\p{InLatin-1Supplement}]").matcher(input);
    while (matcher.find()) {
        matcher.appendReplacement(resultBuffer,
            characterMap.computeIfAbsent(matcher.group(),
                s -> Normalizer.normalize(s, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "")));
    }
    matcher.appendTail(resultBuffer);
    return resultBuffer.toString();
}

Несколько моментов:

  1. Потребности должны быть расширены до ваших потребностей. Normalizerполезно для акцентированных символов, но у вас могут быть и другие. Кроме того, извлеките characterMapout (остерегайтесь того, что calculateIfAbsent обновляет карту, остерегайтесь параллелизма!)
  2. Pattern.compile() не следует вызывать повторно, извлеките его в статический
Другие вопросы по тегам