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

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

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

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

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

5 ответов


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

    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

Так вы сначала снимите diactrical знаков, преобразование в ASCII. Не-ASCII символы становятся знаками вопроса.


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


предостережение: я не знаю Java. Немного о наборах символов.

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

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

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

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

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


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

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

если вы используете Класс нормализатора только для удаления акцентов / диакритики из строк, есть и другой способ. Вы можете использовать Apache commons Lang library (ver. 3), содержащую StringUtils методом stripAccents():

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

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


один isssue с Нормализатором является то, что pre Java 1.6 его в солнце.текстовый пакет тогда как в 1.6 его в java.текстовый пакет и подпись метода it изменились. Поэтому, если ваше приложение neeeds для работы на обеих платформах, вам придется использовать отражение.

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