Замінити всі випадки рядка за допомогою StringBuilder?


79

Мені чогось не вистачає, або StringBuilder не має тієї самої функції "замінити всі випадки рядка A на рядок B", як це робить звичайний клас String? Функція заміни StringBuilder не зовсім однакова. Чи є спосіб зробити це більш ефективно, не генеруючи кілька рядків, використовуючи звичайний клас String?


download.oracle.com/javase/1.5.0/docs/api/java/lang/... Я не знаю, чи щось мені не вистачає, але ця функція, здається, не існує.
meteoritepanama

2
String.replaceAllріч із регулярними виразами ? Я б не турбувався про накладні витрати на перетворення між StringBuilderі String.
Tom Hawtin - таклін

Відповіді:


76

Ну, ви можете написати цикл:

public static void replaceAll(StringBuilder builder, String from, String to)
{
    int index = builder.indexOf(from);
    while (index != -1)
    {
        builder.replace(index, index + from.length(), to);
        index += to.length(); // Move to the end of the replacement
        index = builder.indexOf(from, index);
    }
}

Зверніть увагу, що в деяких випадках він може бути швидшим у використанні lastIndexOf, працюючи ззаду. Я підозрюю, що це так, якщо ви замінюєте довгий рядок коротким - тому, коли ви починаєте, будь-які заміни мають менше копіювати. У будь-якому випадку, це має дати вам вихідну точку.


1
Зверніть увагу, що у випадку, якщо fromі toмає різну довжину, це рішення буде переміщувати хвіст буфера при кожному заміні. Це може бути досить неефективним для довгого буфера з великою кількістю випадків заміни. Відповідь Рона Ромеро не має цього недоліку, але передбачає пошук одного регулярного виразу. Я думаю, що буде швидше, залежить від випадку використання.
Вадзім

While-Loop не потрібно:builder = builder.replace(builder.indexOf(from), builder.indexOf(from) + from.length(), to);
Amitabha Roy

1
@AmitabhaRoy: Це замінить одне , а не всі випадки, як зазначено у питанні.
Джон Скіт,

34

Ви можете використовувати Pattern / Matcher . З javadocs Matcher:

 Pattern p = Pattern.compile("cat");
 Matcher m = p.matcher("one cat two cats in the yard");
 StringBuffer sb = new StringBuffer();
 while (m.find()) {
     m.appendReplacement(sb, "dog");
 }
 m.appendTail(sb);
 System.out.println(sb.toString());

це саме те, що я шукав. Tnx!
dierre

5
Це майже те саме, що Matcher # replaceAll ().
Шеннон

Це працює для "собаки", але загалом недостатньо, оскільки рядок заміни має спеціальні символи. Якщо ви маєте намір використовувати зворотні посилання у значеннях заміщення, тоді вам потрібно уникнути всіх інших зворотних скісних рисок та $. Якщо вам взагалі не потрібно посилатися на відповідний рядок, ви можете просто запустити текст заміни Matcher.quoteReplacement(...). Отжеm.appendReplacement(sb, Matcher.quoteReplacement(someText));
AndrewF

14

@Adam: Я думаю, у своєму фрагменті коду вам слід відстежувати початкову позицію для m.find (), оскільки заміна рядка може змінити зміщення після останнього відповідного символу.

public static void replaceAll(StringBuilder sb, Pattern pattern, String replacement) {
    Matcher m = pattern.matcher(sb);
    int start = 0;
    while (m.find(start)) {
        sb.replace(m.start(), m.end(), replacement);
        start = m.start() + replacement.length();
    }
}

Гадаю, ти маєш рацію. Мені доведеться перевірити, з чим я закінчився.
Адам Гент

13

Подивіться на JavaDoc методу replaceAll класу String :

Замінює кожен підрядок цього рядка, який відповідає даному регулярному виразу, даною заміною. Виклик цього методу форми str.replaceAll (регулярний вираз, repl) дає точно такий же результат, як і вираз

java.util.regex.Pattern.compile (регулярний вираз) .matcher (str) .replaceAll (repl)

Як бачите, ви можете використовувати Pattern і Matcher для цього.


11

Клас org.apache.commons.lang3.text.StrBuilderв Apache Commons Lang дозволяє замінити:

public StrBuilder replaceAll(String searchStr, String replaceStr)

* Це отримує не регулярний вираз, а простий рядок.


1
Зараз це застаріло.
afxentios

1
org.apache.commons.text.StringSubstitutor - чудова бібліотека для такого типу робіт, яка не є застарілою. Але працює на Strings, а не на StringBuilder.
Тед Кейхол

Ви можете використовувати org.apache.commons.text.TextStringBuilder в org.apache.commons.commons-text замість цього
ihebiheb

6

Навіть простий - це сама функція String ReplaceAll. Ви можете написати це як

StringBuilder sb = new StringBuilder("Hi there, are you there?")
System.out.println(Pattern.compile("there").matcher(sb).replaceAll("niru"));

1
Метод replaceAll повертає String. Тож він збирається генерувати екземпляр у пулі рядків. Це не ефективний підхід, коли потрібно зробити кілька різних замін.
AbhishekB

3

Так. Це дуже простий у використанні String.replaceAll()метод:

package com.test;

public class Replace {

    public static void main(String[] args) {
        String input = "Hello World";
        input = input.replaceAll("o", "0");
        System.out.println(input);
    }
}

Вихід:

Hell0 W0rld

Якщо ви дійсно хочете використовувати StringBuilder.replace(int start, int end, String str)замість цього, ось вам:

public static void main(String args[]) {
    StringBuilder sb = new StringBuilder("This is a new StringBuilder");

    System.out.println("Before: " + sb);

    String from = "new";
    String to = "replaced";
    sb = sb.replace(sb.indexOf(from), sb.indexOf(from) + from.length(), to);

    System.out.println("After: " + sb);
}

Вихід:

Before: This is a new StringBuilder
After: This is a replaced StringBuilder

3
Питання було про replaceAll у класі StringBuilder, а не в класі String.
das Keks,

Запитання стосується replaceAll () за допомогою класу StringBuilder.
Sachin Sridhar

2

java.util.regex.Pattern.matcher (CharSequence s) можна використовувати StringBuilder в якості аргументу , так що ви можете знайти і замінити кожне входження вашого шаблону використовуючи початок () і кінець () без виклику builder.toString ()


2

Використовуйте наступне:

/**
* Utility method to replace the string from StringBuilder.
* @param sb          the StringBuilder object.
* @param toReplace   the String that should be replaced.
* @param replacement the String that has to be replaced by.
* 
*/
public static void replaceString(StringBuilder sb,
                                 String toReplace,
                                 String replacement) {      
    int index = -1;
    while ((index = sb.lastIndexOf(toReplace)) != -1) {
        sb.replace(index, index + toReplace.length(), replacement);
    }
}

Чи не переходить це в нескінченний цикл, якщо заміна містить toReplace?
abhilash

1

Ось на місці replaceAll, який змінить передане в StringBuilder. Я думав, що опублікую це, оскільки хотів замінити All на створення нового рядка.

public static void replaceAll(StringBuilder sb, Pattern pattern, String replacement) {
    Matcher m = pattern.matcher(sb);
    while(m.find()) {
        sb.replace(m.start(), m.end(), replacement);
    }
}

Я був шокований, наскільки простий код для цього (з якоїсь причини я думав, що зміна StringBuilder під час використання збігу призведе до початку / кінця групи, але це не так).

Це, швидше за все, швидше, ніж інші відповіді на регулярні вирази, тому що шаблон вже скомпільовано, і ви не створюєте новий рядок, але я не проводив порівняльний аналіз.


0

Як щодо створення методу і дозвольте String.replaceAllзробити це за вас:

public static void replaceAll(StringBuilder sb, String regex, String replacement)
{
    String aux = sb.toString();
    aux = aux.replaceAll(regex, replacement);
    sb.setLength(0);
    sb.append(aux);     
}

Це дуже неефективне використання пам'яті / виділень.
afollestad

0
public static String replaceCharsNew(String replaceStr,Map<String,String> replaceStrMap){
        StringBuilder replaceStrBuilder = new StringBuilder(replaceStr);
        Set<String> keys=replaceStrMap.keySet();
        for(String invalidChar:keys){
            int index = -1;
            while((index=replaceStrBuilder.indexOf(invalidChar,index)) !=-1){
                replaceStrBuilder.replace(index,index+invalidChar.length(),replaceStrMap.get(invalidChar));
            }
        }
        return replaceStrBuilder.toString();
    }

Ви дійсно повинні додати пояснення, чому цей код повинен працювати - ви також можете додати коментарі до самого коду - в його поточній формі він не надає жодних пояснень, які можуть допомогти решті спільноти зрозуміти, що ви зробили для вирішення /Дайте відповідь на запитання.
ishmaelMakitla

У мене був такий сценарій, що мені потрібно замінити кілька недійсних символів на деякі символи. Я просто підправив трохи вищевказаного коду і хотів опублікувати його @JUnitTest public void testReplaceCharsNew () {Map <String, String> map = new HashMap <String, String> (); map.put (",", "/"); map.put (".", ""); map.put (";", "/"); Рядок s = Utils.replaceCharsNew ("тест; Замінити, Знак, Новий.", Карта); assertEquals ("test / Replace / Chars / New", s); }
ramesh

0

Я знайшов такий метод: Matcher.replaceAll (Заміна рядка); У java.util.regex.Matcher.java ви можете побачити більше:

 /**
 * Replaces every subsequence of the input sequence that matches the
 * pattern with the given replacement string.
 *
 * <p> This method first resets this matcher.  It then scans the input
 * sequence looking for matches of the pattern.  Characters that are not
 * part of any match are appended directly to the result string; each match
 * is replaced in the result by the replacement string.  The replacement
 * string may contain references to captured subsequences as in the {@link
 * #appendReplacement appendReplacement} method.
 *
 * <p> Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in
 * the replacement string may cause the results to be different than if it
 * were being treated as a literal replacement string. Dollar signs may be
 * treated as references to captured subsequences as described above, and
 * backslashes are used to escape literal characters in the replacement
 * string.
 *
 * <p> Given the regular expression <tt>a*b</tt>, the input
 * <tt>"aabfooaabfooabfoob"</tt>, and the replacement string
 * <tt>"-"</tt>, an invocation of this method on a matcher for that
 * expression would yield the string <tt>"-foo-foo-foo-"</tt>.
 *
 * <p> Invoking this method changes this matcher's state.  If the matcher
 * is to be used in further matching operations then it should first be
 * reset.  </p>
 *
 * @param  replacement
 *         The replacement string
 *
 * @return  The string constructed by replacing each matching subsequence
 *          by the replacement string, substituting captured subsequences
 *          as needed
 */
public String replaceAll(String replacement) {
    reset();
    StringBuffer buffer = new StringBuffer(input.length());
    while (find()) {
        appendReplacement(buffer, replacement);
    }
    return appendTail(buffer).toString();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.