Jak zamienić podciągi literałów bez rozróżniania wielkości liter w języku Java

133

Korzystając z metody replace(CharSequence target, CharSequence replacement)w String, w jaki sposób mogę sprawić, aby w miejscu docelowym nie była rozróżniana wielkość liter?

Na przykład sposób, w jaki to działa teraz:

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

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

Jak sprawić, by zamień (lub jeśli istnieje bardziej odpowiednia metoda) nie rozróżniał wielkości liter, tak aby oba przykłady zwracały „Bar”?

J. Lin
źródło

Odpowiedzi:

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

Wynik:

Bar

Warto wspomnieć, że replaceAlltraktuje pierwszy argument jako wzorzec wyrażenia regularnego, co może powodować nieoczekiwane rezultaty. Aby rozwiązać ten problem, użyj również Pattern.quotezgodnie z sugestią w komentarzach.

lukastymo
źródło
1
Co się stanie, jeśli cel zawiera $ lub znaki diakrytyczne, takie jak á?
stracktracer
3
Mam na myśli dwie rzeczy: 1. „blÁÜ123” .replaceAll („(? I) bláü”) niczego nie zastępuje. 2. „Zdanie! End” .replaceAll („(? I) Zdanie.”) Może zastąpić więcej niż oczekiwano.
stracktracer
1
Nie możesz zamienić łańcucha na wyrażenie regularne, aby dopasować go tak prosto. Generalnie nie jest to poprawne, będzie działać tylko w określonych przypadkach.
Żeglarz naddunajski
19
Użyj Pattern.quote (), aby zabezpieczyć szukany ciąg znaków przed interpretacją jako wyrażeniem regularnym. Ta łania nie odnosi się do dziwactw Unicode wymienionych powyżej, ale powinna być odpowiednia dla podstawowych zestawów znaków. np. target.replaceAll("(?i)"+Pattern.quote("foo"), "");
Jeff Adamson
1
Tylko się upewniam. Pattern.quote („foo”) nie jest potrzebny, jeśli ciąg znaków to „foo”, prawda? Tylko jeśli jest to coś bardziej wyszukanego, prawda?
ed22
12

Jeśli nie przejmujesz się wielkością liter, być może nie ma znaczenia, czy zwraca wszystkie wielkie litery:

target.toUpperCase().replace("FOO", "");
Poduszkowiec pełen węgorzy
źródło
Możesz również przekazać Locale do toUpperCase (locale), jeśli masz do czynienia z postaciami takimi jak á.
obrabować
10

Może nie tak elegancki jak inne podejścia, ale jest dość solidny i łatwy do naśladowania, szczególnie. dla osób nowszych w Javie. Jedną rzeczą, która mnie przekonuje o klasie String, jest to: istnieje od bardzo dawna i chociaż obsługuje globalne zastępowanie wyrażeniami regularnymi i globalne zastępowanie ciągami (przez CharSequences), to ostatnie nie ma prostego parametru boolowskiego : „isCaseInsensitive”. Naprawdę, można by pomyśleć, że wystarczy dodać ten jeden mały przełącznik, aby uniknąć wszystkich problemów, które jego brak powoduje szczególnie dla początkujących. Teraz w JDK 7, String nadal nie obsługuje tego jednego małego dodatku!

W każdym razie, przestanę się skarżyć. Dla wszystkich, zwłaszcza nowszych w Javie, oto twoja deus ex machina do wycinania i wklejania . Jak powiedziałem, nie jest tak elegancki i nie wygra żadnych nagród za zgrabne kodowanie, ale działa i jest niezawodny. Wszelkie komentarze, nie krępuj się. (Tak, wiem, StringBuffer jest prawdopodobnie lepszym wyborem do zarządzania dwiema liniami mutacji ciągu znaków, ale dość łatwo jest zamienić techniki).

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
źródło
ta metoda jest całkowicie powolna, ponieważ jej złożoność wynosi O (size_str * size_findtext)
Mladen Adamovic
9

Wyrażenia regularne są dość skomplikowane w zarządzaniu ze względu na fakt, że niektóre znaki są zarezerwowane: na przykład "foo.bar".replaceAll(".")tworzy pusty ciąg, ponieważ kropka oznacza „cokolwiek”. Jeśli chcesz zamienić tylko punkt, należy wskazać go jako parametr "\\.".

Prostszym rozwiązaniem jest użycie obiektów StringBuilder do wyszukiwania i zastępowania tekstu. Potrzeba dwóch: jeden, który zawiera tekst zapisany małymi literami, a drugi zawiera wersję oryginalną. Wyszukiwanie odbywa się na małych literach, a wykryty indeks zastąpi również oryginalny tekst.

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
źródło
1
Działa świetnie! Zwróć uwagę, że „target” nie może mieć wartości NULL. Wyczyszczenie sbSourceLower nie powinno być (już) konieczne.
msteiger
Dzięki za zwięzłe rozwiązanie i podziękowania dla @msteiger za korektę. Zastanawiam się, dlaczego nikt nie dodał podobnego rozwiązania do żadnej znanej biblioteki, takiej jak Guava, Apache Commons itp.?
yetanothercoder
4

W przypadku znaków innych niż Unicode:

String result = Pattern.compile("(?i)препарат", 
Pattern.UNICODE_CASE).matcher(source).replaceAll("БАД");
MisterParser
źródło
4

org.apache.commons.lang3.StringUtils:

public static String replaceIgnoreCase (String tekst, String searchString, String zamiennik)

Wielkość liter nieuwzględniająca zastępuje wszystkie wystąpienia String w innym String.

Michał
źródło
4

Po prostu uprość to bez bibliotek innych firm:

    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
źródło
3

Lubię Lifting „s odpowiedź , która wykorzystuje replaceAllz wyrażenia regularnego. Jeśli zamierzasz wielokrotnie wykonywać tę samą zamianę, warto raz wstępnie skompilować wyrażenie regularne:

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
źródło