Manipulação de Strings (um pouquinho menos óbvio que o habitual) [Resolvido]

Olá pessoal.

Estou tentando montar uma classe que valide uma blackList, entretanto, estou com uma dificuldade.

Vou citar um exemplo:

Quando eu insiro a frase: Se não quer peixe baia%cu, vai tomar no %cu.

baia%cu tem que ficar normal, mas %cu tem que sair.

como estou fazendo agora (simplificado):

String texto = "Se não quer peixes baiac%us, vai tomar no %cu.";

String itemBlackList = "%cu";

texto = texto.toLowerCase().replaceAll(itemBlackList, "*******");

O resultado é:
System.out.println(texto);

Se não quer peixes baia*******s, vai tomar no *******."

quando deveria ser
Se não quer peixes baia%cus, vai tomar no *******."

O que eu deveria fazer?

Já tentei:

Pattern textoBuscar = Pattern.compile(itemBlackList.toLowerCase());
Matcher textoFonte;
textoFonte = textoBuscar.matcher(texto.toLowerCase());
texto = textoFonte.replaceAll("*****");

Porém, apresentou o mesmo resultado.

Desde já, agradeço a todos.

Solução ultra-meia-boca:

texto = texto.toLowerCase().replaceAll("\\W" + itemBlackList + "\\W", "*******");  

O curinga \W casa com qualquer caracter “não-palavra”, como espaços e pontuações. Ou seja, ele vai pegar seu palavrão quando nao estiver fazendo parte de outra palavra.

Essa solução tem algumas limitações:

  • Se a palavrinha mágica estiver no começo ou final da frase (sem ponto), não vai funcionar pois não existem os caracteres non-word antes e depois.
  • Ele vai comer o espaço e o ponto na hora de substituir.
    Enfim, é uma porcaria, eu avisei, mas coloquei porque talvez dê alguma idéia para você conseguir soluções melhores :)´

[EDIT] A propósito, para que é esse percent ? [/EDIT]

Então gomesrod, o percent é que eu achei que aqui ia bloquear a palavra rs, só por isso !!! rs

Tô testando aqui a solução que vc me passou, daqui a pouco posto o resultado.

A sua classe é para colocar na blacklist qualquer palavrão ou somente essa do exemplo? Bom, para esse caso, a sugestão que eu dou é você fazer um split na sua String e percorrer o array que resultará. Se a String daquela posição do array for exatamente igual a string da itemblacklist você muda para a String *******. Depois você monta sua String de novo.

O ruim é só a eficiência que isso ficaria…

Só um exemplo do que eu tava falando… eu testei aqui e funcionou. Aí se for para mais palavrões, talvez teira que usar um banco de dados ou um arquivo. Mas não sei o tamanho da sua aplicação e se for grande talvez esse código seja pouco eficiente… mas não tenho muita experiência, hehehe

Segue o código:

[code] public static void main(String[] args){

    String texto  = "Se não quer peixes baiacu, vai tomar no cu";

    String [] split = texto.split(" ");

    for(int i = 0;i < split.length; i++){
        if(split[i].equals("cu")){
            split[i] = "*****";
        }
    }
    StringBuffer sb = new StringBuffer();
    for(int i = 0; i < split.length; i++){
        sb.append(split[i]);
        sb.append(" ");
    }

    System.out.println(sb);
}

[/code]

Saída:

Se não quer peixes baiacu, vai tomar no *****

Bom pessoal, obrigado pela ajuda.
Usei um pouquinho do que vcs me passaram, e cheguei ao seguinte resultado:

public class ValidaEntradaBlackList {
	
	private List<BlackList> itensBlackList;
	
	
	public String validaTexto(String texto, EntityManager em){		
		buscarItensBlackList(em);

		for (int i = 0; i < itensBlackList.size() ; i++) {
			texto = texto.toLowerCase().replaceAll("^" + itensBlackList.get(i).getItem().toLowerCase(), "*******");
			texto = texto.toLowerCase().replaceAll("\\W" + itensBlackList.get(i).getItem().toLowerCase()+ "\\W", " ******* ");
			texto = texto.toLowerCase().replaceAll(itensBlackList.get(i).getItem().toLowerCase().trim() + "\\W$", "*******");
		}
		return texto;
	}
	
	public void buscarItensBlackList(EntityManager em){
		itensBlackList = (List<BlackList>)em.createQuery("select blk from BlackList blk").getResultList();
	}
}

Apenas explicando, esta é uma classe alternativa onde eu tô pasando um EntityManager e a String que eu quero valdidar.

Busco todos os itens da blackList no banco no método buscarItensBlackList() e depois, comparo item por item no método validaTexto().

Não sei se é a melhor maneira de fazer, mas, pegou bonitinho o que eu precisava.

Nesta linha:

texto = texto.toLowerCase().replaceAll("^" + itensBlackList.get(i).getItem().toLowerCase(), "*******");

Pega uma palavra da blackList que esteja no INICIO da frase no texto que eu estou passando.

Nesta outra:

texto = texto.toLowerCase().replaceAll("\\W" + itensBlackList.get(i).getItem().toLowerCase()+ "\\W", " ******* ");

Pega uma palavra da blackList que não seja NEM INICIAL e NEM FINAL de frase no texto que eu estou passando.

E, por ultimo:

texto = texto.toLowerCase().replaceAll(itensBlackList.get(i).getItem().toLowerCase().trim() + "\\W$", "*******");

Pega uma palavra da blackList que esteja no FINAL da frase no texto que eu estou passando.

Acho que é isso.

Muito obrigado pela ajuda de douglas_vidotto e gomesrod.

Você poderia usar um “boundary matcher”.

\b A word boundary
\B A non-word boundary

Exemplo:

import java.util.regex.*;

class Blacklist {
    public static void main(String[] args) {
        String[] blacklist = {
            "rubinho", "massa", 
            "ronaldinhos?", // note que bate com "ronaldinho" e "ronaldinhos" mas não com "ronaldinha"
            "lula"};
        String[] testes = {
            "O óbvio ululante que ninguém quis ver: Rubinho é o novo campeão",
            "A cultura de massas e o presidente Lula",
            "Lula ganhou um boné do Ronaldinho",
            "Felipe Massa e sua amizade com o Ronaldinho Gaúcho: saiba dos fatos",
            "O Galvão Bueno diz ser amigo do Rubinho e dos Ronaldinhos",
            "Juliana Massa nega ser a mais nova Ronaldinha"
        };
        StringBuilder strPattern = new StringBuilder(); 
        boolean first = true;
	for (String b : blacklist) {
            if (!first) {
                strPattern.append ("|");
            } else {
                first = false;
            }
            strPattern.append ("\\b" + b + "\\b");
        }        
        Pattern pattern = Pattern.compile (strPattern.toString(), Pattern.CASE_INSENSITIVE);
        for (String teste : testes) {
            System.out.println ("Original:   " + teste);
            System.out.println ("Censurado:  " + pattern.matcher (teste).replaceAll ("***"));
            System.out.println ();
        }        
    }
}

Boa a sua sugestão enantiomero , mas, como eu faço a conversão de String teste para String texto[]?

é que, eu recebo uma String chamada texto e o for pede um array ou um iterator, então, teria que fazer este cast de String texto para String texto[].

Vlw

Agora sim, um código mais limpo e funcionando perfeitamente:

public class ValidaEntradaBlackList {
	
	private List<BlackList> itensBlackList;
	
	public String validaTexto(String texto, EntityManager em){		
		buscarItensBlackList(em);

		for (int i = 0; i < itensBlackList.size() ; i++) {
			texto = texto.toLowerCase().replaceAll("\\b" + itensBlackList.get(i).getItem().toLowerCase() + "\\b", "*******");
		}
		return texto;
	}
	
	public void buscarItensBlackList(EntityManager em){
		itensBlackList = (List<BlackList>)em.createQuery("select blk from BlackList blk").getResultList();
	}
}

Obrigado a todo mundo que ajudou.