¿Reemplazar todas las apariciones de una cadena usando StringBuilder?

79

¿Me falta algo, o StringBuilder carece de la misma función "reemplazar todas las apariciones de una cadena A con una cadena B" que la clase String normal? La función de reemplazo de StringBuilder no es exactamente la misma. ¿Hay alguna forma de hacerlo de manera más eficiente sin generar múltiples cadenas utilizando la clase String normal?

meteorito panama
fuente
download.oracle.com/javase/1.5.0/docs/api/java/lang/… No sé si me falta algo, pero esa función no parece existir.
meteoritepanama
2
String.replaceAllla cosa con las expresiones regulares ? No me preocuparía por la sobrecarga de convertir entre StringBuildery String.
Tom Hawtin - tackline

Respuestas:

76

Bueno, puedes escribir un bucle:

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

Tenga en cuenta que en algunos casos puede ser más rápido de usar si se lastIndexOftrabaja desde atrás. Sospecho que ese es el caso si está reemplazando una cadena larga por una corta, por lo que cuando llega al principio, cualquier reemplazo tiene menos que copiar. De todos modos, esto debería darte un punto de partida.

Jon Skeet
fuente
1
Tenga en cuenta que en caso de que fromy totengan una longitud diferente, esta solución movería la cola del búfer en cada reemplazo. Esto puede ser bastante ineficaz para un búfer largo con muchas ocurrencias de reemplazo. La respuesta de Ron Romero no tiene esta deficiencia, pero implica una búsqueda de expresiones regulares únicas. Lo que sería más rápido depende del caso de uso, supongo.
Vadzim
Mientras que el bucle no es necesario:builder = builder.replace(builder.indexOf(from), builder.indexOf(from) + from.length(), to);
Amitabha Roy
1
@AmitabhaRoy: Eso reemplazará una ocurrencia, no todas las ocurrencias como se indica en la pregunta.
Jon Skeet
34

Puede utilizar Pattern / Matcher . Desde los 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());
Ron Romero
fuente
Esto es exactamente lo que estaba buscando. Tnx!
dierre
5
Esto es casi lo mismo que Matcher # replaceAll ().
Shannon
Esto funciona para "perro" pero es insuficiente en el caso general porque la cadena de reemplazo tiene caracteres especiales. Si tiene la intención de utilizar referencias inversas en los valores de reemplazo, debe escapar de todas las demás barras invertidas y $. Si no necesita hacer referencia a la cadena coincidente en absoluto, puede ejecutar el texto de reemplazo Matcher.quoteReplacement(...). Entoncesm.appendReplacement(sb, Matcher.quoteReplacement(someText));
AndrewF
14

@Adam: Creo que en su fragmento de código debe rastrear la posición de inicio de m.find () porque el reemplazo de la cadena puede cambiar el desplazamiento después de que el último carácter coincida.

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
fuente
Creo que tienes razón. Tendré que comprobar con qué terminé.
Adam Gent
13

Mire JavaDoc del método replaceAll de la clase String :

Reemplaza cada subcadena de esta cadena que coincide con la expresión regular dada con el reemplazo dado. Una invocación de este método de la forma str.replaceAll (regex, repl) produce exactamente el mismo resultado que la expresión

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

Como puede ver, puede usar Pattern y Matcher para hacer eso.

Alfredo Osorio
fuente
11

La clase org.apache.commons.lang3.text.StrBuilderen Apache Commons Lang permite reemplazos:

public StrBuilder replaceAll(String searchStr, String replaceStr)

* Esto no recibe una expresión regular sino una cadena simple.

Pablo Vargas
fuente
1
Eso ahora está en desuso.
afxentios
1
org.apache.commons.text.StringSubstitutor es una gran biblioteca para este tipo de trabajo que no está en desuso. Pero funciona en Strings, no en StringBuilder.
Ted Cahall
Puede usar org.apache.commons.text.TextStringBuilder en org.apache.commons.commons-text en su lugar
ihebiheb
6

Incluso uno simple está usando la función String ReplaceAll en sí. Puedes escribirlo como

StringBuilder sb = new StringBuilder("Hi there, are you there?")
System.out.println(Pattern.compile("there").matcher(sb).replaceAll("niru"));
usuario2995215
fuente
1
El método replaceAll devuelve String. Entonces va a generar una instancia en el grupo de cadenas. Este no es un enfoque eficiente cuando hay que realizar múltiples reemplazos diferentes.
AbhishekB
3

Si. Es un String.replaceAll()método muy simple de usar :

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

Salida:

Hell0 W0rld

Si realmente quieres usar StringBuilder.replace(int start, int end, String str)en su lugar, aquí tienes:

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

Salida:

Before: This is a new StringBuilder
After: This is a replaced StringBuilder
Amitabha Roy
fuente
3
La pregunta era sobre replaceAll en la clase StringBuilder, no en la clase String.
das Keks
La pregunta trata sobre replaceAll () usando la clase StringBuilder.
Sachin Sridhar
2

Utilice lo siguiente:

/**
* 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
fuente
¿No entra esto en un bucle infinito si el reemplazo contiene toReplace?
abhilash
1

Aquí hay un replaceAll en su lugar que modificará el pasado en StringBuilder. Pensé que publicaría esto porque estaba buscando reemplazar todo sin crear una nueva cadena.

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

Me sorprendió lo simple que era el código para hacer esto (por alguna razón pensé que cambiar el StringBuilder mientras usaba el matcher arrojaría el inicio / final del grupo, pero no es así).

Esto es probablemente más rápido que las otras respuestas de expresiones regulares porque el patrón ya está compilado y no está creando una nueva cadena, pero no hice ninguna evaluación comparativa.

Adam Gent
fuente
0

¿Qué tal crear un método y dejar String.replaceAllque lo haga por usted?

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);     
}
cristiano
fuente
Este es un uso muy ineficiente de memoria / asignaciones.
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
fuente
Realmente debería agregar alguna explicación sobre por qué este código debería funcionar; también puede agregar comentarios en el código en sí; en su forma actual, no proporciona ninguna explicación que pueda ayudar al resto de la comunidad a comprender lo que hizo para resolver Respondo la pregunta.
ishmaelMakitla
Tuve el siguiente escenario en el que necesito reemplazar varios caracteres no válidos con algunos caracteres. Acabo de modificar un poco el código anterior y quería publicarlo @JUnitTest public void testReplaceCharsNew () {Map <String, String> map = new HashMap <String, String> (); map.put (",", "/"); map.put (".", ""); map.put (";", "/"); Cadena s = Utils.replaceCharsNew ("prueba; Reemplazar, Caracteres, Nuevo.", Mapa); assertEquals ("prueba / Reemplazar / Caracteres / Nuevo", s); }
ramesh
0

Encontré este método: Matcher.replaceAll (reemplazo de cadena); En java.util.regex.Matcher.java puedes ver más:

 /**
 * 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
fuente