2011-10-05 23 views
8

Dire che ho un file, che contiene del testo. Ci sono sottostringhe come "substr1", "substr2", "substr3" ecc. Devo sostituire tutte queste sottostringhe con un altro testo, come "repl1", "repl2", "repl3". In Python, vorrei creare un dizionario come questo:Sostituire più sottostringhe alla volta

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

e creare il modello che unisce i tasti con '|', quindi sostituire con re.sub la funzione. C'è un modo semplice simile per farlo in Java?

+0

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

risposta

14

Questo è il modo in cui il Python-suggestione si traduce in 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 

Questo approccio fa un Simultanious sostituzione (vale a dire "in una sola volta"). Vale a dire, se è capitato di avere

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

allora questo approccio darebbe "a b" -> "b c" in contrasto con le risposte che suggeriscono dovrebbero catena diverse chiamate alla replace o replaceAll che darebbe "c c".


(Se si generalizza questo approccio per creare l'espressione regolare programatically, assicuratevi di Pattern.quote ogni ricerca individuale parola e Matcher.quoteReplacement ogni parola sostitutiva.)

+0

In che modo questo approccio differisce da StringUtils.replaceEach? O è sostitutivoOgni come replaceAll? –

+0

Questo approccio è più generale in quanto è possibile fornire una funzione di sostituzione arbitraria (guarda la riga 'm.appendReplacement'). In secondo luogo, non richiede di includere una libreria di terze parti per il gusto di una routine di manipolazione delle stringhe. (Se dipendi già dai Comuni di Apache, o non ti preoccupi affatto di un'altra dipendenza, allora segui l'approccio 'replaceEach'.) – aioobe

+0

(No,' replaceEach' non è lo stesso di 'replaceAll'.' ReplaceAll' è solo una versione regexp di 'replace'.) – aioobe

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

+1 ... Non è "tutto in una volta" però. Se l'esempio fosse diverso, dì "a" -> "b" 'e" "b" -> "c" 'allora non ci sarebbe' b's nel risultato, anche se c'erano 'a's nell'input. – aioobe

+0

Sembra abbastanza brutto, ma grazie comunque :) –

+0

@aioobe: 'StringUtils.replaceEach()' gestisce questo bene. – palacsint

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

-1. Questo non è tutto in una volta e usa inutilmente un metodo regex (replaceAll) al posto del metodo String semplice (replace). – Boann

1

In primo luogo, una dimostrazione del problema:

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

Questo è destinato a sostituire gatti => cani e cani => pappagallini, ma la sostituzione sequenziale opera sul risultato della precedente r eplacement, quindi l'uscita sfortunata è:

Ho tre panieri e due pappagallini.

Ecco la mia implementazione di un metodo di sostituzione simultanea. E 'facile scrivere utilizzando 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(); 
} 

Testing:

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

uscita:

ho tre cani e due pappagallini.

Inoltre, a volte è utile quando si effettua la sostituzione simultanea, per assicurarsi di cercare la corrispondenza più lunga. (La funzione PHP strtr fa questo, per esempio.) Ecco la mia implementazione per questo:

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

Perché avresti bisogno di questo? Esempio segue:

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

uscita:

Java è quello di JavaScript come Ham è quello di Hamster

Se avessimo usato simultaneousReplace al posto del simultaneousReplaceLongest, l'uscita avrebbe avuto "HamScript" invece di "Hamster" :)

Si noti che i metodi di cui sopra fanno distinzione tra maiuscole e minuscole. Se hai bisogno di versioni maiuscole e minuscole è facile modificare quanto sopra perché String.regionMatches può prendere un parametro ignoreCase.

Problemi correlati