2011-10-05 14 views
8

Digamos que tengo un archivo que contiene algo de texto. Hay subcadenas como "substr1", "substr2", "substr3", etc. en ella. Necesito reemplazar todas las subcadenas con algún otro texto, como "repl1", "repl2", "repl3". En Python, me gustaría crear un diccionario de esta manera:Reemplazar múltiples subcadenas a la vez

{ 
"substr1": "repl1", 
"substr2": "repl2", 
"substr3": "repl3" 
} 

y crear el patrón de unirse a las teclas con '|', luego vuelva a colocar con re.sub función. ¿Existe una forma similar similar de hacer esto en Java?

+0

Tema relacionado - http://stackoverflow.com/questions/2049528/java-best-way-for-string-find-and-replace – adatapost

Respuesta

14

así es como el Python-sugestión se traduce en Java:

Map<String, String> replacements = new HashMap<String, String>() {{ 
    put("substr1", "repl1"); 
    put("substr2", "repl2"); 
    put("substr3", "repl3"); 
}}; 

String input = "lorem substr1 ipsum substr2 dolor substr3 amet"; 

// create the pattern joining the keys with '|' 
String regexp = "substr1|substr2|substr3"; 

StringBuffer sb = new StringBuffer(); 
Pattern p = Pattern.compile(regexp); 
Matcher m = p.matcher(input); 

while (m.find()) 
    m.appendReplacement(sb, replacements.get(m.group())); 
m.appendTail(sb); 


System.out.println(sb.toString()); // lorem repl1 ipsum repl2 dolor repl3 amet 

Este enfoque hace un reemplazo Simultanious (es decir, "a la vez"). Es decir, si usted pasó a tener

"a" -> "b" 
"b" -> "c" 

continuación, este enfoque daría "a b" -> "b c" en oposición a las respuestas que sugieren deberían cadena varias llamadas a replace o replaceAll que daría "c c".


(Si se generaliza este enfoque para crear la expresión regular mediante programación, asegúrese de que Pattern.quote cada palabra de búsqueda y Matcher.quoteReplacement cada palabra de sustitución.)

+0

¿Cómo difiere este enfoque de StringUtils.replaceEach? ¿O es replaceEach lo mismo que replaceAll? –

+0

Este enfoque es más general ya que puede proporcionar una función de reemplazo arbitraria (consulte la línea 'm.appendReplacement'). En segundo lugar, no es necesario que incluya una biblioteca de terceros por el bien de una rutina de manipulación de cadenas. (Si ya depende de Apache Commons, o no se molesta en absoluto con otra dependencia, vaya con el enfoque 'replaceEach') – aioobe

+0

(No,' replaceEach' no es lo mismo que 'replaceAll'.' ReplaceAll' es solo una versión regular de 'replace'.) – aioobe

2
yourString.replace("substr1", "repl1") 
      .replace("substr2", "repl2") 
      .replace("substr3", "repl3"); 
+4

+1 ... Sin embargo, eso no es "todo a la vez". Si el ejemplo era diferente, di '" a "->" b "' y '" b "->" c "' luego no habría 'b's en el resultado, aunque hubo' a's en la entrada. – aioobe

+0

Parece bastante feo, pero gracias de todos modos :) –

+0

@aioobe: 'StringUtils.replaceEach()' maneja esto bien. – palacsint

-1
return yourString.replaceAll("substr1","relp1"). 
        replaceAll("substr2","relp2"). 
        replaceAll("substr3","relp3") 
+0

-1. Esto no es todo a la vez, y usa innecesariamente un método regex (replaceAll) en lugar del método String simple (replace). – Boann

1

En primer lugar, una manifestación del problema:

String s = "I have three cats and two dogs."; 
s = s.replace("cats", "dogs") 
    .replace("dogs", "budgies"); 
System.out.println(s); 

Esto está destinado a reemplazar cats => dogs and dogs => budgies, pero la sustitución secuencial opera sobre el resultado de la r anterior eplacement, por lo que la salida desafortunada es:

Tengo tres periquitos y dos periquitos.

Aquí está mi implementación de un método de reemplazo simultáneo. Es fácil escribir usando String.regionMatches:

public static String simultaneousReplace(String subject, String... pairs) { 
    if (pairs.length % 2 != 0) throw new IllegalArgumentException(
     "Strings to find and replace are not paired."); 
    StringBuilder sb = new StringBuilder(); 
    int numPairs = pairs.length/2; 
    outer: 
    for (int i = 0; i < subject.length(); i++) { 
     for (int j = 0; j < numPairs; j++) { 
      String find = pairs[j * 2]; 
      if (subject.regionMatches(i, find, 0, find.length())) { 
       sb.append(pairs[j * 2 + 1]); 
       i += find.length() - 1; 
       continue outer; 
      } 
     } 
     sb.append(subject.charAt(i)); 
    } 
    return sb.toString(); 
} 

Pruebas:

String s = "I have three cats and two dogs."; 
s = simultaneousReplace(s, 
    "cats", "dogs", 
    "dogs", "budgies"); 
System.out.println(s); 

Salida:

tengo tres perros y dos periquitos.

Además, a veces es útil cuando se realiza un reemplazo simultáneo, para asegurarse de buscar la coincidencia más larga. (La función strtr de PHP hace esto, por ejemplo.) Aquí está mi implementación para eso:

public static String simultaneousReplaceLongest(String subject, String... pairs) { 
    if (pairs.length % 2 != 0) throw new IllegalArgumentException(
     "Strings to find and replace are not paired."); 
    StringBuilder sb = new StringBuilder(); 
    int numPairs = pairs.length/2; 
    for (int i = 0; i < subject.length(); i++) { 
     int longestMatchIndex = -1; 
     int longestMatchLength = -1; 
     for (int j = 0; j < numPairs; j++) { 
      String find = pairs[j * 2]; 
      if (subject.regionMatches(i, find, 0, find.length())) { 
       if (find.length() > longestMatchLength) { 
        longestMatchIndex = j; 
        longestMatchLength = find.length(); 
       } 
      } 
     } 
     if (longestMatchIndex >= 0) { 
      sb.append(pairs[longestMatchIndex * 2 + 1]); 
      i += longestMatchLength - 1; 
     } else { 
      sb.append(subject.charAt(i)); 
     } 
    } 
    return sb.toString(); 
} 

¿Por qué necesita esto? Ejemplo es el siguiente:

String truth = "Java is to JavaScript"; 
truth += " as " + simultaneousReplaceLongest(truth, 
    "Java", "Ham", 
    "JavaScript", "Hamster"); 
System.out.println(truth); 

Salida:

Java es tener JavaScript como jamón es a Hamster

Si hubiéramos utilizado simultaneousReplace en lugar de simultaneousReplaceLongest, la salida habría tenido "HamScript" en lugar de "Hamster" :)

Tenga en cuenta que los métodos anteriores distinguen entre mayúsculas y minúsculas. Si necesita versiones que no distingan entre mayúsculas y minúsculas, es fácil modificar lo anterior porque String.regionMatches puede tomar un parámetro ignoreCase.

Cuestiones relacionadas