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"?

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.


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", "");

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

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", "*"));
    }
}

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("БАД");

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.


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

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));
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.