Как заменить непечатаемые символы Unicode в Java?


88

Следующие символы заменят управляющие символы ASCII (сокращение для [\x00-\x1F\x7F]):

my_string.replaceAll("\\p{Cntrl}", "?");

Следующее заменит все непечатаемые символы ASCII (сокращение для [\p{Graph}\x20]), включая символы с диакритическими знаками:

my_string.replaceAll("[^\\p{Print}]", "?");

Однако ни то, ни другое не работает для строк Unicode. Есть ли у кого-нибудь хороший способ удалить непечатаемые символы из строки Unicode?


2
В качестве дополнения: список общих категорий Unicode можно найти в UAX # 44
McDowell


1
@Stewart: привет, вы смотрели на вопрос / ответы помимо заголовка?!?
dagnelies

1
@Stewart: этот другой вопрос касается только подмножества ascii непечатаемых символов !!!
dagnelies

Ответы:


134
my_string.replaceAll("\\p{C}", "?");

Подробнее о регулярном выражении Unicode . java.util.regexPattern/ String.replaceAllподдерживает их.


По крайней мере, в java 1.6 они не поддерживаются. download.oracle.com/javase/6/docs/api/java/util/regex/… ... Я также попробовал вашу строку, и, помимо отсутствия обратной косой черты, она просто не работает.
dagnelies

Это работает: char c = 0xFFFA; String.valueOf(c).replaceAll("\\p{C}", "?");также в javadoc for pattern look в разделе поддержки Unicode говорится, что он поддерживает категории
Op De Cirkel

Ты прав! Приношу извинения. Я не заметил этого, потому что мне пришлось добавить категории Zl Zp, поскольку они в основном были источником проблем. Работает отлично. Не могли бы вы отредактировать свой пост, чтобы я мог проголосовать за него снова?
dagnelies 01

6
Есть также невидимые пробельные символы (например, 0x0200B), которые являются частью группы \ p {Zs}. К сожалению, здесь есть и обычные пробелы. Для тех, кто пытается отфильтровать строку ввода, которая не должна содержать пробелов, строка s.replaceAll("[\\p{C}\\p{Z}]", "")подойдет для вас
Андрей Л

1
Это то, что я искал, я пытался, replaceAll("[^\\u0000-\\uFFFF]", "")но безуспешно
Bibaswann Bandyopadhyay

58

Оп Де Сиркель в основном прав. Его предложение сработает в большинстве случаев:

myString.replaceAll("\\p{C}", "?");

Но если он myStringможет содержать кодовые точки, отличные от BMP, тогда все сложнее. \p{C}содержит суррогатные кодовые точки \p{Cs}. Приведенный выше метод замены повредит кодовые точки, отличные от BMP, иногда заменяя только половину суррогатной пары. Возможно, это ошибка Java, а не предполагаемое поведение.

Возможен вариант использования других составляющих категорий:

myString.replaceAll("[\\p{Cc}\\p{Cf}\\p{Co}\\p{Cn}]", "?");

Однако одиночные суррогатные символы, не входящие в пару (каждому суррогатному символу назначен код), не будут удалены. Подход без регулярного выражения - единственный известный мне способ правильно обработать \p{C}:

StringBuilder newString = new StringBuilder(myString.length());
for (int offset = 0; offset < myString.length();)
{
    int codePoint = myString.codePointAt(offset);
    offset += Character.charCount(codePoint);

    // Replace invisible control characters and unused code points
    switch (Character.getType(codePoint))
    {
        case Character.CONTROL:     // \p{Cc}
        case Character.FORMAT:      // \p{Cf}
        case Character.PRIVATE_USE: // \p{Co}
        case Character.SURROGATE:   // \p{Cs}
        case Character.UNASSIGNED:  // \p{Cn}
            newString.append('?');
            break;
        default:
            newString.append(Character.toChars(codePoint));
            break;
    }
}

8

Возможно, вас заинтересуют категории Unicode «Другое, Контроль» и, возможно, «Другое, Формат» (к сожалению, последний, похоже, содержит как непечатаемые, так и печатные символы).

В регулярных выражениях Java вы можете проверить их, используя \p{Cc}и \p{Cf}соответственно.


Что ж, в плохих java-выражениях их нет, но, по крайней мере, я получил список прямо сейчас ... лучше, чем ничего. спасибо
dagnelies 01

4

методы удара для вашей цели

public static String removeNonAscii(String str)
{
    return str.replaceAll("[^\\x00-\\x7F]", "");
}

public static String removeNonPrintable(String str) // All Control Char
{
    return str.replaceAll("[\\p{C}]", "");
}

public static String removeSomeControlChar(String str) // Some Control Char
{
    return str.replaceAll("[\\p{Cntrl}\\p{Cc}\\p{Cf}\\p{Co}\\p{Cn}]", "");
}

public static String removeFullControlChar(String str)
{
    return removeNonPrintable(str).replaceAll("[\\r\\n\\t]", "");
} 

0

Я использовал для этого эту простую функцию:

private static Pattern pattern = Pattern.compile("[^ -~]");
private static String cleanTheText(String text) {
    Matcher matcher = pattern.matcher(text);
    if ( matcher.find() ) {
        text = text.replace(matcher.group(0), "");
    }
    return text;
}

Надеюсь, это будет полезно.


0

Основываясь на ответах Op De Cirkel и noackjr , для общей очистки строк я делаю следующее: 1. обрезка начальных или конечных пробелов, 2. dos2unix, 3. mac2unix, 4. удаление всех «невидимых символов Unicode», кроме пробелов:

myString.trim.replaceAll("\r\n", "\n").replaceAll("\r", "\n").replaceAll("[\\p{Cc}\\p{Cf}\\p{Co}\\p{Cn}&&[^\\s]]", "")

Протестировано с помощью Scala REPL.


0

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

private String removeNonBMPCharacters(final String input) {
    StringBuilder strBuilder = new StringBuilder();
    input.codePoints().forEach((i) -> {
        if (Character.isSupplementaryCodePoint(i)) {
            strBuilder.append("?");
        } else {
            strBuilder.append(Character.toChars(i));
        }
    });
    return strBuilder.toString();
}

-4

Я переделал код для номеров телефонов +9 (987) 124124 Извлечение цифр из строки в Java

 public static String stripNonDigitsV2( CharSequence input ) {
    if (input == null)
        return null;
    if ( input.length() == 0 )
        return "";

    char[] result = new char[input.length()];
    int cursor = 0;
    CharBuffer buffer = CharBuffer.wrap( input );
    int i=0;
    while ( i< buffer.length()  ) { //buffer.hasRemaining()
        char chr = buffer.get(i);
        if (chr=='u'){
            i=i+5;
            chr=buffer.get(i);
        }

        if ( chr > 39 && chr < 58 )
            result[cursor++] = chr;
        i=i+1;
    }

    return new String( result, 0, cursor );
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.