Як замінити нечутливі до регістру буквальні підрядки в 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"

Як я можу зробити так, щоб це замінило (або якщо є більш підходящий метод) нечутливим до регістру, щоб обидва приклади повернули "Бар"?

Відповіді:


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. "Речення! Кінець". Замінити Все ("(? I) Речення.") Може замінити більше, ніж передбачалося.
— stracktracer

1
Ви не можете перетворити рядок у регулярний вираз, який відповідає йому настільки просто. Загалом це неправильно, воно працюватиме лише для конкретних випадків.
— Дунайський матрос

19
Використовуйте Pattern.quote (), щоб захистити рядок пошуку від інтерпретації як регулярний вираз. Цей соплі-соплі вирішують перелічені вище прикмети unicode, але для основних наборів символів має бути добре. напр. target.replaceAll("(?i)"+Pattern.quote("foo"), "");
— Джефф Адамсон

1
Просто переконуюсь. Pattern.quote ("foo") не потрібен, якщо рядок "foo" правильно? Тільки якщо це щось більш вигадливе, правда?
— ed22


10

Не такий елегантний, як інші підходи, але досить міцний і простий у дотриманні, особливо. для людей, які новіші для Java. Одне, що мене цікавить про клас String, це: це існує вже дуже давно, і він підтримує глобальну заміну на регулярний вираз і глобальну заміну на Strings (через 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
Чудово працює! Зауважте, що "ціль" не повинна бути нульовою. Очищення sbSourceLower не повинно бути необхідним (більше).
— msteiger

Дякуємо за стисле рішення та дякуємо @msteiger за виправлення. Цікаво, чому ніхто не додав подібного рішення жодному відомому лібюру, як Guava, Apache Commons тощо?
— іншийкодер

4

Для символів, які не використовуються Unicode:

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

4

org.apache.commons.lang3.StringUtils:

загальнодоступний статичний рядок замінаIgnoreCase (текст рядка, пошук рядківString, заміна рядка)

Випадок нечутливо замінює всі виникнення рядка в іншій рядку.


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.