Alle Vorkommen eines Strings mit StringBuilder ersetzen?

78

Fehlt mir etwas oder fehlt StringBuilder die gleiche Funktion "Alle Vorkommen eines Strings A durch String B ersetzen" wie die normale String-Klasse? Die StringBuilder-Ersetzungsfunktion ist nicht ganz dieselbe. Gibt es eine Möglichkeit, dies effizienter zu gestalten, ohne mehrere Strings mit der normalen String-Klasse zu generieren?

Meteoritenpanama
quelle
download.oracle.com/javase/1.5.0/docs/api/java/lang/… Ich weiß nicht, ob mir etwas fehlt, aber diese Funktion scheint nicht zu existieren.
Meteoritepanama
2
String.replaceAlldie Sache mit Regexs ? Ich würde mir keine Sorgen über den Aufwand beim Konvertieren zwischen StringBuilderund machen String.
Tom Hawtin - Tackline

Antworten:

76

Nun, Sie können eine Schleife schreiben:

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

Beachten Sie, dass die Verwendung in einigen Fällen lastIndexOfvon hinten schneller sein kann . Ich vermute, dass dies der Fall ist, wenn Sie eine lange Zeichenfolge durch eine kurze ersetzen. Wenn Sie also am Anfang stehen, müssen alle Ersetzungen weniger kopiert werden. Auf jeden Fall sollte dies Ihnen einen Ausgangspunkt geben.

Jon Skeet
quelle
1
Beachten Sie, dass für den Fall , wenn fromund tounterschiedliche Längen hat diese Lösung Puffer Schwanz auf jedem ersetzen bewegen würde. Dies kann für lange Puffer mit vielen Ersetzungsereignissen ziemlich unwirksam sein. Ron Romeros Antwort hat dieses Manko nicht, sondern beinhaltet eine einzelne Regexp-Suche. Was schneller wäre, hängt wohl vom Anwendungsfall ab.
Vadzim
While-Loop ist nicht erforderlich:builder = builder.replace(builder.indexOf(from), builder.indexOf(from) + from.length(), to);
Amitabha Roy
1
@AmitabhaRoy: Dies ersetzt ein Vorkommen, nicht alle Vorkommen, wie in der Frage angegeben.
Jon Skeet
34

Sie können Pattern / Matcher verwenden . Aus den Matcher-Javadocs:

 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());
Ron Romero
quelle
Genau das habe ich gesucht. Tnx!
Dierre
5
Dies ist fast dasselbe wie bei Matcher # replaceAll ().
Shannon
Dies funktioniert für "Hund", ist jedoch im allgemeinen Fall unzureichend, da die Ersatzzeichenfolge Sonderzeichen enthält. Wenn Sie Rückverweise in den Ersetzungswerten verwenden möchten, müssen Sie alle anderen Backslashes und umgehen $. Wenn Sie überhaupt nicht auf die übereinstimmende Zeichenfolge verweisen müssen, können Sie den Ersatztext einfach durchlaufen Matcher.quoteReplacement(...). Alsom.appendReplacement(sb, Matcher.quoteReplacement(someText));
AndrewF
14

@Adam: Ich denke, in Ihrem Code-Snippet sollten Sie die Startposition für m.find () verfolgen, da das Ersetzen von Zeichenfolgen den Versatz nach dem letzten übereinstimmenden Zeichen ändern kann.

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();
    }
}
fir99
quelle
Ich glaube, Du hast recht. Ich muss überprüfen, was ich am Ende hatte.
Adam Gent
13

Schauen Sie sich JavaDoc der replaceAll- Methode der String- Klasse an:

Ersetzt jeden Teilstring dieser Zeichenfolge, der dem angegebenen regulären Ausdruck entspricht, durch den angegebenen Ersatz. Ein Aufruf dieser Methode der Form str.replaceAll (Regex, Repl) ergibt genau das gleiche Ergebnis wie der Ausdruck

java.util.regex.Pattern.compile (regex) .matcher (str) .replaceAll (repl)

Wie Sie sehen können, können Sie dazu Pattern und Matcher verwenden.

Alfredo Osorio
quelle
11

Die Klasse org.apache.commons.lang3.text.StrBuilderin Apache Commons Lang ermöglicht das Ersetzen von:

public StrBuilder replaceAll(String searchStr, String replaceStr)

* Dies erhält keinen regulären Ausdruck, sondern eine einfache Zeichenfolge.

Paul Vargas
quelle
1
Das ist jetzt veraltet.
Afxentios
1
org.apache.commons.text.StringSubstitutor ist eine großartige Bibliothek für diese Art von Arbeit, die nicht veraltet ist. Funktioniert aber mit Strings, nicht mit StringBuilder.
Ted Cahall
Sie können org.apache.commons.text.TextStringBuilder stattdessen in org.apache.commons.commons-text verwenden
ihebiheb
6

Selbst einfach ist es, die String ReplaceAll-Funktion selbst zu verwenden. Sie können es schreiben als

StringBuilder sb = new StringBuilder("Hi there, are you there?")
System.out.println(Pattern.compile("there").matcher(sb).replaceAll("niru"));
user2995215
quelle
1
Die Methode replaceAll gibt String zurück. Es wird also eine Instanz im String-Pool generiert. Dies ist kein effizienter Ansatz, wenn mehrere verschiedene Ersetzungen vorgenommen werden müssen.
AbhishekB
3

Ja. Es ist sehr einfach, String.replaceAll()Methode zu verwenden :

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

Ausgabe:

Hell0 W0rld

Wenn Sie StringBuilder.replace(int start, int end, String str)stattdessen wirklich verwenden möchten, können Sie loslegen:

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

Ausgabe:

Before: This is a new StringBuilder
After: This is a replaced StringBuilder
Amitabha Roy
quelle
3
Die Frage betraf replaceAll in der StringBuilder-Klasse, nicht in der String-Klasse.
das Keks
Bei der Frage geht es um replaceAll () mit der StringBuilder-Klasse.
Sachin Sridhar
2

java.util.regex.Pattern.matcher (CharSequence s) kann einen StringBuilder als Argument verwenden, sodass Sie jedes Vorkommen Ihres Musters mit start () und end () finden und ersetzen können, ohne builder.toString () aufzurufen.

Pierre
quelle
2

Verwenden Sie Folgendes:

/**
* 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);
    }
}
Anandan
quelle
1

Hier ist ein vorhandenes replaceAll, das das in StringBuilder übergebene ändert. Ich dachte, ich würde dies posten, da ich ersetzen wollte, ohne einen neuen String zu erstellen.

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

Ich war schockiert, wie einfach der Code dafür war (aus irgendeinem Grund dachte ich, dass das Ändern des StringBuilder während der Verwendung des Matchers den Start / Ende der Gruppe auslösen würde, aber dies ist nicht der Fall).

Dies ist wahrscheinlich schneller als die anderen Regex-Antworten, da das Muster bereits kompiliert ist und Sie keinen neuen String erstellen, aber kein Benchmarking durchgeführt habe.

Adam Gent
quelle
0

Wie wäre es, eine Methode zu erstellen und String.replaceAllsie für Sie erledigen zu lassen:

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);     
}
Christian
quelle
Dies ist eine sehr ineffiziente Nutzung von Speicher / Zuordnungen.
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();
    }
ramesh
quelle
Sie sollten wirklich eine Erklärung hinzufügen, warum dieser Code funktionieren sollte - Sie können auch Kommentare in den Code selbst einfügen - in seiner aktuellen Form enthält er keine Erklärung, die dem Rest der Community helfen kann, zu verstehen, was Sie zur Lösung getan haben /beantworte die Frage.
ishmaelMakitla
Ich hatte das folgende Szenario, in dem ich mehrere ungültige Zeichen durch einige Zeichen ersetzen muss. Ich habe gerade ein bisschen den obigen Code optimiert und wollte ihn veröffentlichen @JUnitTest public void testReplaceCharsNew () {Map <String, String> map = new HashMap <String, String> (); map.put (",", "/"); map.put (".", ""); map.put (";", "/"); String s = Utils.replaceCharsNew ("test; Replace, Chars, New.", Map); assertEquals ("test / Replace / Chars / New", s); }
Ramesh
0

Ich habe diese Methode gefunden: Matcher.replaceAll (String-Ersetzung); In java.util.regex.Matcher.java sehen Sie mehr:

 /**
 * 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();
}
Codios
quelle