Come sostituire sottostringhe letterali senza distinzione tra maiuscole e minuscole in Java


130

Utilizzando il metodo replace(CharSequence target, CharSequence replacement)in String, come posso rendere il target senza distinzione tra maiuscole e minuscole?

Ad esempio, il modo in cui funziona in questo momento:

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

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

Come posso farlo sostituirlo (o se esiste un metodo più adatto) non fa distinzione tra maiuscole e minuscole in modo che entrambi gli esempi restituiscano "Bar"?

Risposte:


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

Produzione:

Bar

Vale la pena ricordare che replaceAlltratta il primo argomento come un modello regex, che può causare risultati inaspettati. Per risolvere questo problema, utilizzare anche Pattern.quotecome suggerito nei commenti.


1
E se il target contiene $ o caratteri diacritici come á?
stracktracer,

3
Intendo due cose: 1. "blÁÜ123" .replaceAll ("(? I) bláü") non sostituisce nulla. 2. "Frase! Fine" .replaceAll ("(? I) Frase.") Forse sostituisce più del previsto.
stracktracer,

1
Non puoi trasformare la stringa in regex abbinandola così semplice. Non è corretto in generale, funzionerà solo per casi specifici.
Danubian Sailor,

19
Usa Pattern.quote () per proteggere la stringa di ricerca dall'interpretazione come regex. Questo doot affronta le stranezze unicode sopra elencate, ma dovrebbe andare bene per i set di caratteri di base. ad es. target.replaceAll("(?i)"+Pattern.quote("foo"), "");
Jeff Adamson,

1
Solo per essere sicuro. Pattern.quote ("pippo") non è necessario se la stringa è "pippo" giusto? Solo se è qualcosa di più elegante, giusto?
ed22

10

Se non ti interessa il caso, allora forse non importa se restituisce tutto upcase:

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

Puoi anche passare le Impostazioni internazionali a UpperCase (impostazioni locali) se hai a che fare con personaggi come á.
rapina il

10

Forse non elegante come altri approcci ma è piuttosto solido e facile da seguire, esp. per le persone più recenti a Java. Una cosa che mi colpisce della classe String è questa: è in circolazione da molto tempo e mentre supporta una sostituzione globale con regexp e una sostituzione globale con stringhe (tramite CharSequences), quest'ultima non ha un semplice parametro booleano : 'isCaseInsensitive'. Davvero, avresti pensato che solo aggiungendo quel piccolo interruttore, tutti i problemi che la sua assenza causava soprattutto ai principianti avrebbero potuto essere evitati. Ora il JDK 7, String ancora non supporta questa piccola aggiunta!

Bene comunque, smetterò di afferrare. Per tutti, in particolare quelli più recenti di Java, ecco il tuo deus ex machina " taglia e incolla" . Come ho detto, non è così elegante e non ti vincerà nessun premio di programmazione, ma funziona ed è affidabile. Qualsiasi commento, sentiti libero di contribuire. (Sì, lo so, StringBuffer è probabilmente una scelta migliore per gestire le due linee di mutazione della stringa di caratteri, ma è abbastanza facile scambiare le tecniche.)

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

questo metodo è assolutamente lento poiché la sua complessità è O (size_str * size_findtext)
Mladen Adamovic

9

Le espressioni regolari sono piuttosto complesse da gestire a causa del fatto che alcuni caratteri sono riservati: ad esempio, "foo.bar".replaceAll(".")produce una stringa vuota, perché il punto significa "qualsiasi cosa" Se si desidera sostituire solo il punto dovrebbe essere indicato come parametro "\\.".

Una soluzione più semplice è usare gli oggetti StringBuilder per cercare e sostituire il testo. Ne bastano due: uno che contiene il testo in versione minuscola mentre il secondo contiene la versione originale. La ricerca viene eseguita sui contenuti minuscoli e l'indice rilevato sostituirà anche il testo originale.

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
Funziona alla grande! Si noti che "target" non deve essere nullo. La cancellazione di sbSourceLower non dovrebbe essere necessaria (più).
msteiger,

Grazie per la soluzione concisa e grazie a @msteiger per la correzione. Mi chiedo perché nessuno abbia aggiunto una soluzione simile a qualsiasi lib famosa come Guava, Apache Commons ecc.?
Yetanothercoder

4

Per caratteri non Unicode:

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

4

org.apache.commons.lang3.StringUtils:

public static String replaceIgnoreCase (Testo stringa, String searchString, Sostituzione stringa)

Case sostituisce in modo insensibile tutte le occorrenze di una stringa all'interno di un'altra stringa.


3

Mi piace la risposta di smas che usa con un'espressione regolare. Se eseguirai la stessa sostituzione più volte, ha senso pre-compilare l'espressione regolare una volta: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

Semplicemente senza librerie di terze parti:

    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));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.