Как заменить буквальные подстроки без учета регистра в Java


130

Как с помощью метода replace(CharSequence target, CharSequence replacement)String сделать цель нечувствительной к регистру?

Например, как это работает сейчас:

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

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

Как я могу сделать так, чтобы замена (или если есть более подходящий метод) регистронезависима, чтобы оба примера возвращали "Bar"?

Ответы:


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

Вывод:

Bar

Стоит упомянуть, что replaceAllпервый аргумент рассматривается как шаблон регулярного выражения, что может привести к неожиданным результатам. Чтобы решить эту проблему, также используйте, Pattern.quoteкак предложено в комментариях.


1
Что, если цель содержит $ или диакритические символы, такие как á?
stracktracer

3
Я имею в виду две вещи: 1. "blÁÜ123" .replaceAll ("(? I) bláü") ничего не заменяет. 2. «Sentence! End» .replaceAll («(? I) Sentence.»), Возможно, заменяет больше, чем ожидалось.
stracktracer

1
Вы не можете так просто превратить строку в регулярное выражение. В целом это не правильно, работает только в определенных случаях.
Danubian Sailor

19
Используйте Pattern.quote (), чтобы защитить строку поиска от интерпретации как регулярное выражение. Эти сопли устраняют перечисленные выше причуды Unicode, но должны подходить для базовых наборов символов. например target.replaceAll("(?i)"+Pattern.quote("foo"), "");
Джефф Адамсон

1
Всего лишь уточняю. Pattern.quote ("foo") не нужен, если строка "foo", верно? Только если это что-то более навороченное, правда?
ed22


10

Возможно, не так элегантно, как другие подходы, но он довольно надежен и прост в использовании, особенно. для новичков в Java. Одна вещь, которая мне нравится в классе String, заключается в следующем: он существует уже очень давно, и хотя он поддерживает глобальную замену регулярным выражением и глобальную замену строками (через CharSequences), последний не имеет простого логического параметра : 'isCaseInsensitive'. На самом деле, вы могли подумать, что, просто добавив этот маленький переключатель, можно было бы избежать всех проблем, которые вызывает его отсутствие, особенно для новичков. Теперь в JDK 7 String по- прежнему не поддерживает это небольшое дополнение!

Ну, в любом случае, хватит хвататься. Для всех, особенно новичков в Java, вот вам ваш "вырезать и вставить" deus ex machina . Как я уже сказал, он не такой элегантный и не принесет вам никаких призов за программирование, но он работает и надежен. Любые комментарии, не стесняйтесь вносить свой вклад. (Да, я знаю, StringBuffer, вероятно, лучший выбор для управления строками мутации из двух символьных строк, но достаточно легко поменять методы.)

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

этот метод крайне медленный, так как его сложность составляет O (size_str * size_findtext)
Младен Адамович

9

Регулярными выражениями довольно сложно управлять из-за того, что некоторые символы зарезервированы: например, "foo.bar".replaceAll(".")создается пустая строка, потому что точка означает «что угодно». Если вы хотите заменить только точку, следует указать в качестве параметра "\\.".

Более простое решение - использовать объекты StringBuilder для поиска и замены текста. Требуется два: один содержит текст в нижнем регистре, а второй - исходную версию. Поиск выполняется по содержимому в нижнем регистре, и обнаруженный индекс также заменит исходный текст.

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
Прекрасно работает! Обратите внимание, что «target» не может быть нулевым. Очистка sbSourceLower не требуется (больше).
msteiger

Спасибо за краткое решение и спасибо @msteiger за исправление. Интересно, почему никто не добавил подобное решение ни в одну известную библиотеку, такую ​​как Guava, Apache Commons и т. Д.?
stillanothercoder 08

4

Для символов, отличных от Unicode:

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

4

org.apache.commons.lang3.StringUtils:

общедоступная статическая строка replaceIgnoreCase (текст строки, строка searchString, замена строки)

Без учета регистра заменяет все вхождения строки в другую строку.


3

Мне нравится SMAS «ы ответ , который использует 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

Просто сделайте это простым без сторонних библиотек:

    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));
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.