Como posso substituir caracteres Unicode não imprimíveis em Java?

88

O seguinte substituirá os caracteres de controle ASCII (abreviação de [\x00-\x1F\x7F]):

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

O seguinte substituirá todos os caracteres ASCII não imprimíveis (abreviação de [\p{Graph}\x20]), incluindo caracteres acentuados:

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

No entanto, nenhum dos dois funciona para strings Unicode. Alguém tem uma boa maneira de remover caracteres não imprimíveis de uma string Unicode?

dagnelies
fonte
2
Apenas como um adendo: a lista de categorias gerais Unicode pode ser encontrada em UAX # 44
McDowell
1
@Stewart: oi, você olhou as perguntas / respostas além do título?!?
dagnelies
1
@Stewart: essa outra questão cobre apenas o subconjunto ascii de caracteres não imprimíveis !!!
dagnelies

Respostas:

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

Veja mais sobre Unicode regex . java.util.regexPattern/ os String.replaceAllapóia.

Op De Cirkel
fonte
Pelo menos no java 1.6, não há suporte para eles. download.oracle.com/javase/6/docs/api/java/util/regex/… ... Eu também tentei sua linha, e além de não ter uma barra invertida, simplesmente não funciona.
dagnelies 01 de
Isso funciona: char c = 0xFFFA; String.valueOf(c).replaceAll("\\p{C}", "?");também no javadoc para aparência de padrão na seção de suporte a Unicode , diz que suporta as categorias
Op De Cirkel 01 de
Você está certo! Peço desculpas. Não percebi porque tive que adicionar as categorias Zl Zp, uma vez que essas eram principalmente a fonte dos problemas. Funciona perfeitamente. Você poderia fazer uma mini edição em sua postagem para que eu possa votar novamente?
dagnelies 01 de
6
Existem também caracteres de espaço em branco invisíveis (como 0x0200B), que fazem parte do grupo \ p {Zs}. Infelizmente, este também inclui espaços em branco normais. Para aqueles que estão tentando filtrar uma string de entrada que não deve conter espaços, a string s.replaceAll("[\\p{C}\\p{Z}]", "")fará o encanto
Andrey L
1
Isso é o que eu estava procurando, eu estava tentando, replaceAll("[^\\u0000-\\uFFFF]", "")mas não tive sucesso
Bibaswann Bandyopadhyay
58

Op De Cirkel está quase certo. Sua sugestão funcionará na maioria dos casos:

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

Mas se myStringpode conter pontos de código não BMP, então é mais complicado. \p{C}contém os pontos de código substitutos de \p{Cs}. O método de substituição acima corromperá os pontos de código não BMP, às vezes substituindo apenas metade do par substituto. É possível que seja um bug do Java em vez do comportamento pretendido.

Usar as outras categorias constituintes é uma opção:

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

No entanto, personagens substitutos solitários que não fazem parte de um par (cada personagem substituto tem um ponto de código atribuído) não serão removidos. Uma abordagem não regex é a única maneira que conheço para lidar adequadamente com \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
fonte
8

Você pode estar interessado nas categorias Unicode "Outro, Controle" e possivelmente "Outro, Formato" (infelizmente o último parece conter caracteres não imprimíveis e imprimíveis).

Em expressões regulares Java, você pode verificá-las usando \p{Cc}e \p{Cf}respectivamente.

Joachim Sauer
fonte
Bem, uma pena que as expressões java não os têm, mas pelo menos eu tenho a lista agora ... melhor do que nada. obrigado
dagnelies
4

métodos em golpe para seu 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
fonte
0

Usei esta função simples para isso:

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 isso seja útil.

user1300830
fonte
0

Com base nas respostas de Op De Cirkel e noackjr , o seguinte é o que eu faço para limpeza geral de strings: 1. aparando espaços em branco iniciais ou finais, 2. dos2unix, 3. mac2unix, 4. removendo todos os "caracteres Unicode invisíveis", exceto espaços em branco:

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

Testado com Scala REPL.

RyanLeiTaiwan
fonte
0

Eu proponho remover os caracteres não imprimíveis como abaixo, em vez de substituí-los

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
fonte
-4

Reestruturei o código para os números de telefone +9 (987) 124124 Extrai dígitos de uma string em 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
fonte