¿Cómo puedo reemplazar los caracteres Unicode no imprimibles en Java?

88

Lo siguiente reemplazará los caracteres de control ASCII (abreviatura de [\x00-\x1F\x7F] ):

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

Lo siguiente reemplazará todos los caracteres ASCII no imprimibles (abreviatura de [\p{Graph}\x20] ), incluidos los caracteres acentuados:

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

Sin embargo, ninguno de los dos funciona para cadenas Unicode. ¿Alguien tiene una buena manera de eliminar caracteres no imprimibles de una cadena Unicode?

dagnelies
fuente
2
Solo como un apéndice: la lista de Categorías generales Unicode se puede encontrar en UAX # 44
McDowell
1
@ Stewart: hola, ¿has mirado las preguntas / respuestas además del título?
dagnelies
1
@Stewart: ¡esa otra pregunta cubre solo el subconjunto ascii de caracteres no imprimibles!
dagnelies

Respuestas:

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

Más información sobre expresiones regulares Unicode . java.util.regexPattern/ los String.replaceAllapoya.

Op De Cirkel
fuente
En java 1.6 al menos, no hay soporte para ellos. download.oracle.com/javase/6/docs/api/java/util/regex/… ... También probé tu línea, y además de perder una barra invertida, simplemente no funciona.
Dagnelies
Esto funciona: char c = 0xFFFA; String.valueOf(c).replaceAll("\\p{C}", "?");también en el javadoc para el aspecto del patrón en la sección de soporte Unicode , dice que admite las categorías
Op De Cirkel
¡Tienes razón! Me disculpo. No me di cuenta porque tuve que agregar las categorías Zl Zp ya que esas eran principalmente la fuente de problemas. Funciona perfectamente. ¿Podrías hacer una pequeña edición de tu publicación para que pueda votar de nuevo?
dagnelies
6
También hay espacios en blanco invisibles (como 0x0200B), que forman parte del grupo \ p {Zs}. Desafortunadamente, este también incluye espacios en blanco normales. Para aquellos que están tratando de filtrar una cadena de entrada que no debe contener ningún espacio, la cadena s.replaceAll("[\\p{C}\\p{Z}]", "")hará el encanto
Andrey L
1
Esto es lo que estaba buscando, lo estaba intentando replaceAll("[^\\u0000-\\uFFFF]", "")pero no tuve éxito
Bibaswann Bandyopadhyay
58

Op De Cirkel tiene razón. Su sugerencia funcionará en la mayoría de los casos:

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

Pero si myStringpuede contener puntos de código que no son BMP, entonces es más complicado. \p{C}contiene los puntos de código sustitutos de \p{Cs}. El método de reemplazo anterior corrompe los puntos de código que no son BMP reemplazando a veces solo la mitad del par sustituto. Es posible que se trate de un error de Java en lugar de un comportamiento previsto.

Usar las otras categorías constituyentes es una opción:

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

Sin embargo, los personajes sustitutos solitarios que no forman parte de un par (cada personaje sustituto tiene un punto de código asignado) no se eliminarán. Un enfoque sin expresiones regulares es la única forma que conozco de manejar correctamente \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;
    }
}
noackjr
fuente
8

Puede que le interesen las categorías Unicode "Otro, Control" y posiblemente "Otro, Formato" (desafortunadamente, este último parece contener caracteres imprimibles y no imprimibles).

En las expresiones regulares de Java, puede verificarlas usando \p{Cc}y \p{Cf}respectivamente.

Joachim Sauer
fuente
Bueno, lástima que las expresiones java no las tengan, pero al menos tengo la lista ahora mismo ... mejor que nada. gracias
dagnelies
4

métodos en golpe para tu objetivo

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]", "");
} 
Ali Bagheri
fuente
0

He usado esta función simple para esto:

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;
}

Espero que esto sea de utilidad.

usuario1300830
fuente
0

Según las respuestas de Op De Cirkel y noackjr , lo siguiente es lo que hago para la limpieza general de cadenas: 1. recortar los espacios en blanco iniciales o finales, 2. dos2unix, 3. mac2unix, 4. eliminar todos los "caracteres Unicode invisibles" excepto los espacios en blanco:

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

Probado con Scala REPL.

RyanLeiTaiwan
fuente
0

Propongo que elimine los caracteres no imprimibles como a continuación en lugar de reemplazarlos

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();
}
Ramesh Bathini
fuente
-4

He rediseñado el código para los números de teléfono +9 (987) 124124 Extraer dígitos de una cadena en 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 );
}
Kairat Koibagarov
fuente