Como substituir substrings literais que não diferenciam maiúsculas de minúsculas em Java


130

Usando o método replace(CharSequence target, CharSequence replacement)em String, como posso tornar o destino sem distinção entre maiúsculas e minúsculas?

Por exemplo, a maneira como funciona agora:

String target = "FooBar";
target.replace("Foo", "") // would return "Bar"

String target = "fooBar";
target.replace("Foo", "") // would return "fooBar"

Como fazê-lo substituir (ou se houver um método mais adequado) não diferencia maiúsculas de minúsculas, de modo que ambos os exemplos retornem "Bar"?

Respostas:


284
String target = "FOOBar";
target = target.replaceAll("(?i)foo", "");
System.out.println(target);

Resultado:

Bar

Vale ressaltar que replaceAlltrata o primeiro argumento como um padrão regex, o que pode causar resultados inesperados. Para resolver isso, use também o Pattern.quotesugerido nos comentários.


1
E se o destino contiver $ ou caracteres diacríticos como á?
Stracktracer

3
Quero dizer duas coisas: 1. "blÁÜ123" .replaceAll ("(? I) bláü") não substitui nada. 2. "Sentença! Fim" .replaceAll ("(? I) Sentença.") Talvez substitua mais do que o previsto.
Stracktracer #

1
Você não pode transformar uma string em regex que corresponda a isso de maneira simples. Geralmente, não está correto, funcionará apenas em casos específicos.
Danubian Sailor

19
Use Pattern.quote () para proteger a cadeia de pesquisa de ser interpretada como uma expressão regular. Este ranho de corça trata das peculiaridades unicode listadas acima, mas deve ser bom para conjuntos de caracteres básicos. por exemplo target.replaceAll("(?i)"+Pattern.quote("foo"), "");
Jeff Adamson

1
Apenas certificando-se. Pattern.quote ("foo") não é necessário se a string for "foo", certo? Só se for algo mais chique, certo?
ed22

10

Se você não se importa com o caso, talvez não importe se ele retorna tudo em ordem:

target.toUpperCase().replace("FOO", "");

Você também pode passar o Local para toUpperCase (locale) se estiver lidando com caracteres como á.
24513 rob

10

Talvez não seja tão elegante quanto outras abordagens, mas é bastante sólido e fácil de seguir, especialmente. para pessoas mais novas em Java. Uma coisa que me impressiona sobre a classe String é a seguinte: ela existe há muito tempo e, embora ofereça suporte a uma substituição global por regexp e uma substituição global por Strings (via CharSequences), essa última não possui um parâmetro booleano simples : 'isCaseInsensitive'. Realmente, você pensaria que, apenas adicionando esse pequeno interruptor, todos os problemas que sua ausência causa para iniciantes, em especial, poderiam ter sido evitados. Agora no JDK 7, o String ainda não suporta esta pequena adição!

Bem, de qualquer maneira, vou parar de me segurar. Para todos, especialmente os mais novos em Java, aqui está o seu deus ex machina recortar e colar . Como eu disse, não é tão elegante e não ganha nenhum prêmio de codificação, mas funciona e é confiável. Quaisquer comentários, fique à vontade para contribuir. (Sim, eu sei, o StringBuffer é provavelmente a melhor escolha para gerenciar as duas linhas de mutação da cadeia de caracteres, mas é fácil o suficiente trocar as técnicas.)

public String replaceAll(String findtxt, String replacetxt, String str, 
        boolean isCaseInsensitive) {
    if (str == null) {
        return null;
    }
    if (findtxt == null || findtxt.length() == 0) {
        return str;
    }
    if (findtxt.length() > str.length()) {
        return str;
    }
    int counter = 0;
    String thesubstr = "";
    while ((counter < str.length()) 
            && (str.substring(counter).length() >= findtxt.length())) {
        thesubstr = str.substring(counter, counter + findtxt.length());
        if (isCaseInsensitive) {
            if (thesubstr.equalsIgnoreCase(findtxt)) {
                str = str.substring(0, counter) + replacetxt 
                    + str.substring(counter + findtxt.length());
                // Failing to increment counter by replacetxt.length() leaves you open
                // to an infinite-replacement loop scenario: Go to replace "a" with "aa" but
                // increment counter by only 1 and you'll be replacing 'a's forever.
                counter += replacetxt.length();
            } else {
                counter++; // No match so move on to the next character from
                           // which to check for a findtxt string match.
            }
        } else {
            if (thesubstr.equals(findtxt)) {
                str = str.substring(0, counter) + replacetxt 
                    + str.substring(counter + findtxt.length());
                counter += replacetxt.length();
            } else {
                counter++;
            }
        }
    }
    return str;
}

esse método é totalmente lento, pois sua complexidade é O (size_str * size_findtext)
Mladen Adamovic

9

Expressões regulares são bastante complexas de gerenciar devido ao fato de alguns caracteres serem reservados: por exemplo, "foo.bar".replaceAll(".")produz uma sequência vazia, porque o ponto significa "qualquer coisa". Se você deseja substituir, apenas o ponto deve ser indicado como parâmetro "\\.".

Uma solução mais simples é usar objetos StringBuilder para pesquisar e substituir texto. São necessários dois: um que contém o texto na versão em minúsculas enquanto o segundo contém a versão original. A pesquisa é realizada no conteúdo em minúsculas e o índice detectado também substituirá o texto original.

public class LowerCaseReplace 
{
    public static String replace(String source, String target, String replacement)
    {
        StringBuilder sbSource = new StringBuilder(source);
        StringBuilder sbSourceLower = new StringBuilder(source.toLowerCase());
        String searchString = target.toLowerCase();

        int idx = 0;
        while((idx = sbSourceLower.indexOf(searchString, idx)) != -1) {
            sbSource.replace(idx, idx + searchString.length(), replacement);
            sbSourceLower.replace(idx, idx + searchString.length(), replacement);
            idx+= replacement.length();
        }
        sbSourceLower.setLength(0);
        sbSourceLower.trimToSize();
        sbSourceLower = null;

        return sbSource.toString();
    }


    public static void main(String[] args)
    {
        System.out.println(replace("xXXxyyyXxxuuuuoooo", "xx", "**"));
        System.out.println(replace("FOoBaR", "bar", "*"));
    }
}

1
Funciona bem! Observe que "destino" não deve ser nulo. A limpeza do sbSourceLower não deve ser mais necessária.
msteiger

Obrigado pela solução concisa e obrigado a @msteiger pela correção. Eu me pergunto por que ninguém adicionou solução semelhante a qualquer lib famosa como Guava, Apache Commons etc.?
yetanothercoder

4

Para caracteres não Unicode:

String result = Pattern.compile("(?i)препарат", 
Pattern.UNICODE_CASE).matcher(source).replaceAll("БАД");

4

org.apache.commons.lang3.StringUtils:

public static String replaceIgnoreCase (Texto da string, String searchString, Substituição da string)

O caso insensivelmente substitui todas as ocorrências de uma String dentro de outra String.


3

Eu gosto da resposta de smas que usa com uma expressão regular. Se você fará a mesma substituição várias vezes, faz sentido pré-compilar a expressão regular uma vez:replaceAll

import java.util.regex.Pattern;

public class Test { 

    private static final Pattern fooPattern = Pattern.compile("(?i)foo");

    private static removeFoo(s){
        if (s != null) s = fooPattern.matcher(s).replaceAll("");
        return s;
    }

    public static void main(String[] args) {
        System.out.println(removeFoo("FOOBar"));
    }
}

3

Simplesmente simplifique sem bibliotecas de terceiros:

    final String source = "FooBar";
    final String target = "Foo";
    final String replacement = "";
    final String result = Pattern.compile(target, Pattern.LITERAL | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE).matcher(source)
.replaceAll(Matcher.quoteReplacement(replacement));
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.