Преобразование Unicode в ASCII без изменения длины строки (в Java)

Каков наилучший способ преобразования строки из Unicode в ASCII без изменения ее длины (что очень важно в моем случае)? Также символы без проблем преобразования должны находиться в тех же позициях, что и в исходной строке. Таким образом, «Ä» необходимо преобразовать в «A», а не что-то загадочное, у которого больше символов.

Редактировать:
@novalis. Такие символы (например, азиатские языки) должны быть просто преобразованы в некоторые заполнители. Меня не интересуют эти слова или что они означают.

@MtnViewMark – я должен сохранить любое количество символов и позицию доступных символов ASCII при любых обстоятельствах.

Вот еще информация: у меня есть инструменты для интеллектуального анализа текста, которые могут обрабатывать только строки ASCII. Большая часть текста, который должен быть обработан, написан на английском языке, но некоторые из них содержат символы не ASCII. Меня не интересуют эти слова, но я должен быть уверен, что слова, которые меня интересуют (те, которые содержат только символы ASCII), находятся на тех же позициях после преобразования строки.

Как указано в этом ответе, следующий код должен работать:

String s = "口水雞 hello Ä"; String s1 = Normalizer.normalize(s, Normalizer.Form.NFKD); String regex = "[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+"; String s2 = new String(s1.replaceAll(regex, "").getBytes("ascii"), "ascii"); System.out.println(s2); System.out.println(s.length() == s2.length()); 

Выход

 ??? hello A true 

Таким образом, вы сначала удаляете диакритические метки, конвертируете в ascii. Символы без знаков ascii станут вопросительными знаками.

Используйте java.text.Normalizer.normalize() с Normalizer.Form.NFD , затем отфильтруйте символы, отличные от ASCII.

Caveat: Я не знаю Java. Немного о наборах символов.

Вы не указываете, какой набор символов вы используете точно.

Но независимо от того, что вы используете, невозможно преобразовать строку Unicode в ASCII и сохранить исходную длину и позиции символа, просто потому, что набор символов Unicode будет использовать несколько байтов для некоторых символов (очевидно).

Единственное исключение, которое я знаю, это строка UTF-8, содержащая только символы ASCII: эта строка уже будет идентичной как в UTF-8, так и в ASCII, потому что UTF-8 использует многобайтовые символы только тогда, когда это необходимо. (Я не знаю о других вариантах Unicode, могут быть и другие динамические).

Единственным обходным решением, которое я вижу, является добавление пробела к любому специальному символу, который был заменен ASCII, но это Go teborg строку ( Göteborg в UTF8 должен стать Go teborg чтобы сохранить длину).

Возможно, вы хотите уточнить, чего вы хотите достичь, поэтому люди здесь могут предложить обходные пути.

Как отметил Пол Тейлор, проблема с использованием Normalizer заключается в том, что вам нужно, чтобы проект был компилируемым / выполняемым в pre-1.6, а также в версии 1.6 и выше. Вы столкнетесь с проблемами, поскольку Normalizer находится в разных пакетах ( java.text.Normalizer (для 1.6) вместо sun.text.Normalizer (для pre-1.6)) и имеет другую сигнатуру метода.

Обычно рекомендуется использовать reflection для вызова соответствующего метода Normalizer.normalize (). ( Пример можно найти здесь ).
Но если вы не хотите вставлять рефлекс в свой код, вы можете использовать библиотеку icu4j . Он содержит class com.ibm.icu.text.Normalizer с методом normalize() который выполняет ту же работу, что и java.text.Normalizer / sun.text.Normalizer. Библиотека Icu имеет (должна иметь) собственную реализацию Normalizer, чтобы вы могли поделиться своим проектом с библиотекой, и это должно быть java-независимым.
Недостатком является то, что библиотека icu довольно большая.

Если вы используете class Normalizer только для удаления акцентов / диакритических знаков из Strings, есть и другой способ. Вы можете использовать библиотеку Apache commons lang (версия 3), которая содержит StringUtils с методом stripAccents() :

 String noAccentsString = org.apache.commons.lang3.StringUtils.stripAccents(s); 

Библиотека Lang3, вероятно, использует reflection для вызова соответствующего Normalizer в соответствии с версией java. Таким образом, преимущество заключается в том, что у вас нет отражения в коде.

Один из них с Normalizer заключается в том, что pre Java 1.6 его в пакете sun.text, тогда как в 1.6 его в пакете java.text и его подпись метода изменилась. Поэтому, если ваше приложение будет работать на обеих платформах, вам придется использовать reflection.

Альтернативное пользовательское решение описывается как технология 3 здесь