2011-09-23 12 views
5

Digamos que tiene la siguiente cadena:Sustitución de múltiples subcadenas en Java cuando el texto de reemplazo se superpone texto de búsqueda

cat dog fish dog fish cat 

Desea reemplazar todas cats con dogs, todo dogs con fish, y todos fish con cats. Intuitivamente, el resultado esperado:

dog fish cat fish cat dog 

Si se intenta la solución obvia, recorriendo con replaceAll(), se obtiene:

  1. (el original) cat dog fish dog fish cat
  2. (CAT -> perro) dog dog fish dog fish dog
  3. (perro -> pescado) fish fish fish fish fish fish
  4. (pescado -> gato) cat cat cat cat cat cat

Claramente, este no es el resultado esperado. Entonces, ¿cuál es la forma más sencilla de hacer esto? Puedo improvisar algo junto con Pattern y Matcher (y un montón de Pattern.quote() y Matcher.quoteReplacement()), pero me niego a creer que soy la primera persona en tener este problema y no hay una función de biblioteca para resolverlo.

(Fwiw, el caso real es un poco más complicado y no implica permutas rectas.)

Respuesta

8

Parece StringUtils.replaceEach en apache commons hace lo que quiere:

StringUtils.replaceEach("abcdeab", new String[]{"ab", "cd"}, new String[]{"cd", "ab"}); 
// returns "cdabecd" 

Tenga en cuenta que la documenent en el enlace de arriba parece ser un error. Ver los comentarios a continuación para más detalles.

+0

Esto parece estar explícitamente prohibido, al menos en StringUtils 2.5 y versiones anteriores: "Lanza: IllegalArgumentException - si la búsqueda se repite y hay un bucle sin fin debido a que las salidas de una son entradas a otra". (Aunque lo que en realidad obtengo es una IllegalStateException ya que la recursión no se detiene correctamente.) –

+0

Estoy confundido. El método que cito arriba (que copié directamente de los javadocs en línea) ni siquiera existe. No hay replaceEach con el último parámetro booleano. Por otro lado 'StringUtils.replaceEach (" abcde ", new String [] {" ab "," cd "}, new String [] {" cd "," ab "})' devuelve '" cdabe "' que parece ser correcto. Lo comprobé en 2.5 –

+0

Ok, algo de claridad. 'replaceEachRepeatedly' arroja' IllegalStateException' como usted escribió. 'replaceEach' con el último parámetro' boolean' no existe. 'replaceEach' sin el último parámetro' boolean' parece hacer el trabajo. –

4

me gustaría crear un StringBuilder y luego analizar el texto vez, una palabra a la vez, transfiriendo sobre palabras sin cambios o palabras cambiadas sobre la marcha. No lo analizaría para cada intercambio como sugieres.

Así que en vez de hacer algo como:

// pseudocode 
text is new text swapping cat with dog 
text is new text swapping dog with fish 
text is new text swapping fish with cat 

lo haría

for each word in text 
    if word is cat, swap with dog 
    if word is dog, swap with fish 
    if word is fish, swap with cat 
    transfer new word (or unchanged word) into StringBuilder. 

probablemente haría un método (...) de intercambio para esto y utilizo un HashMap para el intercambiar.

Por ejemplo

import java.util.HashMap; 
import java.util.Map; 
import java.util.Scanner; 

public class SwapWords { 
    private static Map<String, String> myMap = new HashMap<String, String>(); 

    public static void main(String[] args) { 
     // this would really be loaded using a file such as a text file or xml 
     // or even a database: 
     myMap.put("cat", "dog"); 
     myMap.put("dog", "fish"); 
     myMap.put("fish", "dog"); 

     String testString = "cat dog fish dog fish cat"; 

     StringBuilder sb = new StringBuilder(); 
     Scanner testScanner = new Scanner(testString); 
     while (testScanner.hasNext()) { 
     String text = testScanner.next(); 
     text = myMap.get(text) == null ? text : myMap.get(text); 
     sb.append(text + " "); 
     } 

     System.out.println(sb.toString().trim()); 
    } 
} 
7
String rep = str.replace("cat","§1§").replace("dog","§2§") 
       .replace("fish","§3§").replace("§1§","dog") 
       .replace("§2§","fish").replace("§3§","cat"); 

feo e ineficiente como el infierno, pero las obras.


OK, aquí hay una versión más elaborada y genérica. Prefiero usar una expresión regular en lugar de un escáner. De esa forma puedo reemplazar cadenas arbitrarias, no solo palabras (que pueden ser mejores o peores).De todos modos, aquí va:

public static String replace(
    final String input, final Map<String, String> replacements) { 

    if (input == null || "".equals(input) || replacements == null 
     || replacements.isEmpty()) { 
     return input; 
    } 
    StringBuilder regexBuilder = new StringBuilder(); 
    Iterator<String> it = replacements.keySet().iterator(); 
    regexBuilder.append(Pattern.quote(it.next())); 
    while (it.hasNext()) { 
     regexBuilder.append('|').append(Pattern.quote(it.next())); 
    } 
    Matcher matcher = Pattern.compile(regexBuilder.toString()).matcher(input); 
    StringBuffer out = new StringBuffer(input.length() + (input.length()/10)); 
    while (matcher.find()) { 
     matcher.appendReplacement(out, replacements.get(matcher.group())); 
    } 
    matcher.appendTail(out); 
    return out.toString(); 
} 

Código de prueba:

System.out.println(replace("cat dog fish dog fish cat", 
    ImmutableMap.of("cat", "dog", "dog", "fish", "fish", "cat"))); 

Salida:

perro gato perro pescado pez gato

Obviamente esta solución sólo tiene sentido para muchos reemplazos, de lo contrario es una gran exageración.

+0

1 agradable, me gusta –

+0

@ Eng.Fouad no es posible como una abominación tan feo :-) Me gusta el –

+0

la idea de reemplazar las palabras con palabras temporales –

0
public class myreplase { 
    public Map<String, String> replase; 

    public myreplase() { 
     replase = new HashMap<String, String>(); 

     replase.put("a", "Apple"); 
     replase.put("b", "Banana"); 
     replase.put("c", "Cantalope"); 
     replase.put("d", "Date"); 
     String word = "a b c d a b c d"; 

     String ss = ""; 
     Iterator<String> i = replase.keySet().iterator(); 
     while (i.hasNext()) { 
      ss += i.next(); 
      if (i.hasNext()) { 
       ss += "|"; 
      } 
     } 

     Pattern pattern = Pattern.compile(ss); 
     StringBuilder buffer = new StringBuilder(); 
     for (int j = 0, k = 1; j < word.length(); j++,k++) { 
      String s = word.substring(j, k); 
      Matcher matcher = pattern.matcher(s); 
      if (matcher.find()) { 
       buffer.append(replase.get(s)); 
      } else { 
       buffer.append(s); 
      } 
     } 
     System.out.println(buffer.toString()); 
    } 

    public static void main(String[] args) { 
     new myreplase(); 
    } 
} 

de salida: - Manzana Plátano de Cantalope Fecha de Apple Plátano de Cantalope Fecha

Cuestiones relacionadas