Cómo reemplazar subcadenas literales que no distinguen entre mayúsculas y minúsculas en Java

130

Usando el método replace(CharSequence target, CharSequence replacement)en String, ¿cómo puedo hacer que el objetivo no distinga entre mayúsculas y minúsculas?

Por ejemplo, la forma en que funciona en este momento:

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

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

¿Cómo puedo hacer que reemplazar (o si hay un método más adecuado) no distingue entre mayúsculas y minúsculas para que ambos ejemplos devuelvan "Bar"?

J. Lin
fuente

Respuestas:

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

Salida:

Bar

Vale la pena mencionar que replaceAlltrata el primer argumento como un patrón regex, que puede causar resultados inesperados. Para resolver esto, use también Pattern.quotecomo se sugiere en los comentarios.

lukastymo
fuente
1
¿Qué pasa si el objetivo contiene $ o caracteres diacríticos como á?
Stracktracer
3
Quiero decir dos cosas: 1. "blÁÜ123" .replaceAll ("(? I) bláü") no reemplaza nada. 2. "Sentence! End" .replaceAll ("(? I) Sentence.") Puede reemplazar más de lo previsto.
Stracktracer
1
No puedes convertir una cadena en expresiones regulares que coincidan tan simple. En general, no es correcto, solo funcionará para casos específicos.
Danubian Sailor
19
Use Pattern.quote () para proteger la cadena de búsqueda de ser interpretada como una expresión regular. Esta gama no aborda las peculiaridades Unicode enumeradas anteriormente, pero debería estar bien para los conjuntos de caracteres básicos. por ejemplo target.replaceAll("(?i)"+Pattern.quote("foo"), "");
Jeff Adamson
1
Solo asegurándome. Pattern.quote ("foo") no es necesario si la cadena es "foo" ¿verdad? Solo si es algo más elegante, ¿verdad?
ed22
10

Si no te importa el caso, entonces quizás no importa si devuelve todo en mayúsculas:

target.toUpperCase().replace("FOO", "");
Aerodeslizador lleno de anguilas
fuente
También puede pasar el Locale a toUpperCase (locale) si trata con caracteres como á.
robar
10

Tal vez no sea tan elegante como otros enfoques, pero es bastante sólido y fácil de seguir, especialmente. para personas más nuevas en Java. Una cosa que me sorprende de la clase String es esta: ha existido durante mucho tiempo y, aunque admite un reemplazo global con regexp y un reemplazo global con Strings (a través de CharSequences), este último no tiene un parámetro booleano simple : 'isCaseInsensitive'. Realmente, habrías pensado que con solo agregar ese pequeño interruptor, todos los problemas que su ausencia causa especialmente para los principiantes podrían haberse evitado. Ahora en JDK 7, String still no admite esta pequeña adición!

Bueno, de todos modos, dejaré de agarrarme. Para todos los más nuevos en Java, aquí está su deus ex machina de cortar y pegar . Como dije, no es tan elegante y no te dará ningún premio de codificación, pero funciona y es confiable. Cualquier comentario, no dude en contribuir. (Sí, lo sé, StringBuffer es probablemente una mejor opción para administrar las líneas de mutación de cadena de dos caracteres, pero es bastante fácil intercambiar las 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;
}
Matt Campbell
fuente
este método es completamente lento ya que su complejidad es O (size_str * size_findtext)
Mladen Adamovic
9

Las expresiones regulares son bastante complejas de administrar debido al hecho de que algunos caracteres están reservados: por ejemplo, "foo.bar".replaceAll(".")produce una cadena vacía, porque el punto significa "cualquier cosa". Si desea reemplazar solo el punto debe indicarse como parámetro"\\." .

Una solución más simple es usar objetos StringBuilder para buscar y reemplazar texto. Se necesitan dos: uno que contiene el texto en minúscula, mientras que el segundo contiene la versión original. La búsqueda se realiza en el contenido en minúsculas y el índice detectado también reemplazará el 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", "*"));
    }
}
ilmassa
fuente
1
¡Funciona genial! Tenga en cuenta que "objetivo" no debe ser nulo. Borrar sbSourceLower no debería ser necesario (más).
msteiger
Gracias por una solución concisa y gracias a @msteiger por la corrección. Me pregunto por qué nadie agregó una solución similar a una lib famosa como Guava, Apache Commons, etc.
yetanothercoder
4

Para caracteres no Unicode:

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

org.apache.commons.lang3.StringUtils:

cadena estática pública replaceIgnoreCase (texto de cadena, cadena de búsqueda String, reemplazo de cadena)

Las mayúsculas y minúsculas reemplazan todas las ocurrencias de una Cadena dentro de otra Cadena.

Miguel
fuente
3

Me gusta la respuesta de smas que se usa con una expresión regular. Si va a hacer el mismo reemplazo muchas veces, tiene sentido precompilar la expresión regular una 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"));
    }
}
Stephen Ostermiller
fuente
3

Simplemente hazlo simple sin bibliotecas de terceros:

    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));
gouessej
fuente