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();
}
Несколько моментов:
- Потребности должны быть расширены до ваших потребностей.
Normalizer
полезно для акцентированных символов, но у вас могут быть и другие. Кроме того, извлекитеcharacterMap
out (остерегайтесь того, что calculateIfAbsent обновляет карту, остерегайтесь параллелизма!) - Pattern.compile() не следует вызывать повторно, извлеките его в статический