2011-09-23 18 views
5

Diciamo che avete la seguente stringa:Sostituzione più sottostringhe in Java, quando il testo di sostituzione si sovrappone ricerca

cat dog fish dog fish cat 

Si vuole sostituire tutti cats con dogs, tutte dogs con fish, e tutto fish con cats. Intuitivamente, il risultato atteso:

dog fish cat fish cat dog 

Se si tenta la soluzione più ovvia, scorrendo con replaceAll(), si ottiene:

  1. (originale) cat dog fish dog fish cat
  2. (cat -> cane) dog dog fish dog fish dog
  3. (cane -> pesce) fish fish fish fish fish fish
  4. (pesce -> gatto) cat cat cat cat cat cat

Chiaramente, questo non è il risultato previsto. Quindi qual è il modo più semplice per farlo? Posso mettere insieme qualcosa con Pattern e Matcher (e molto Pattern.quote() e Matcher.quoteReplacement()), ma mi rifiuto di credere che io sia la prima persona ad avere questo problema e che non ci sia alcuna funzione di libreria per risolverlo.

(FWIW, il caso attuale è un po 'più complicato e non comporta swap dritto.)

risposta

8

Sembra StringUtils.replaceEach commons apache fa ciò che si vuole:

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

Si noti che il documenent ai link di cui sopra sembra essere in errore. Vedi i commenti sotto per i dettagli.

+0

Questo sembra essere esplicitamente vietato, almeno in StringUtils 2.5 e precedenti: "Lanci: IllegalArgumentException - se la ricerca si ripete e c'è un ciclo infinito a causa di uscite di un essere entrate in un altro". (Anche se quello che ottengo effettivamente è un IllegalStateException in quanto la ricorsione non riesce a fermarsi correttamente.) –

+0

Sono confuso. Il metodo che cito sopra (che ho copiato direttamente dai javadoc online) non esiste nemmeno. Non c'è nessun rimpiazzo con l'ultimo parametro booleano. D'altra parte 'StringUtils.replaceEach (" abcde ", new String [] {" ab "," cd "}, new String [] {" cd "," ab "})' restituisce '" cdabe "' che sembra per essere corretto. Ho controllato questo su 2.5 –

+0

Ok, un po 'di chiarezza. 'replaceEachRepeatedly' lancia' IllegalStateException' come hai scritto tu. 'replaceEach' con l'ultimo parametro' boolean' non esiste. 'replaceEach' senza l'ultimo parametro' boolean' sembra fare il lavoro. –

4

vorrei creare uno StringBuilder e quindi analizzare il testo volta, una parola alla volta, trasferendo su parole invariate o cambiate parole mentre vado. Non lo analizzerei per ogni scambio come suggerisci.

Quindi, piuttosto che fare qualcosa di simile:

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

farei

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. 

probabilmente sarei fare un metodo (...) di swap per questo e uso un HashMap per la scambiare.

Per esempio

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

brutto e inefficiente come l'inferno, ma opere.


OK, ecco una versione più elaborata e generica. Preferisco usare un'espressione regolare piuttosto che uno scanner. In questo modo posso sostituire stringhe arbitrarie, non solo parole (che possono essere migliori o peggiori).In ogni caso, ecco qui:

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

Codice di prova:

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

uscita:

cane pesce pesce gatto gatto cane

Ovviamente questa soluzione ha senso solo per molti sostituzioni, altrimenti è un enorme overkill.

+0

uno bello, mi piace –

+0

@ Eng.Fouad non è possibile come un abominio come brutto :-) –

+0

Mi piace il idea di sostituire le parole con parole temporanee –

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

uscita: - Mela Banana Cantalope Data Mela Banana Cantalope Data

Problemi correlati