2016-05-15 15 views
11

Sto provando a tradurre i nodi su un albero di nuovo formato e ho problemi a ottenere la sostituzione corretta. Dire che ho il HashMap:Sostituzioni multiple Java su un singolo passaggio

"(1:" : "(30:" 
",1:" : ",30:" 
"(30:" : "(6:" 
",30:" : ",6:" 

E l'albero:

(30:0.07,(1:0.06,2:0.76)) 

La saggezza convenzionale potrebbe suggerire multipla replaceAll, ma questo pone un problema:

replaceAll("(1:", "(30:") >> (30:0.07,(30:0.06,2:0.76)) 
replaceAll("(30:", "(6:") >> (6:0.07,(6:0.06,2:0.76)) 

Il problema qui è che abbiamo' ha sostituito un nodo che è stato precedentemente sostituito. L'albero corretta dovrebbe essere simile:

(6:0.07,(30:0.06,2:0.76)) 

Ora ho già fatto questo in Python:

def multiple_replace(taxa, text): 
    regex = re.compile("|".join(map(re.escape, taxa.keys()))) 
    return regex.sub(lambda mo: taxa[mo.group(0)], text) 

ma sto avendo problemi con la mia implementazione Java:

private String convertTree (String treeOld, HashMap<String, String> conv) { 
     Pattern pattern = Pattern.compile("\\(\\d+:|,\\d+:"); 
     Matcher matcher = pattern.matcher(treeOld); 
     StringBuilder sbt = new StringBuilder(treeOld); 
     while (matcher.find()) { 
      String replace = conv.get(matcher.group()); 
      System.out.println(matcher.group() + "||" +replace + " || " + matcher.start() + ":"+matcher.end()); 
      sbt.delete(matcher.start(), matcher.end()); 
      sbt.insert(matcher.start(), replace); 
     } 
     return treeOld; 

    } 

Mentre la sostituzione sembra funzionare, non riesco a ottenere l'indicizzazione abbastanza corretta con diverse dimensioni di stringhe (come mostrato nell'esempio). C'è un modo per farlo in Java?

risposta

8

È possibile utilizzare Matcher#appendReplacement per modificare la stringa durante l'abbinamento.

Nota che il vostro regex può essere semplificata per [,(]\d+: come tuoi rami alternativi si differenziano solo per il primo carattere ([,(] partite sia , o ().

Ecco un IDEONE demo:

import java.util.*; 
import java.util.regex.*; 
import java.lang.*; 
import java.io.*; 

class Ideone 
{ 
    public static void main (String[] args) throws java.lang.Exception 
    { 
     String tree = "(30:0.07,(1:0.06,2:0.76))"; 
     HashMap<String, String> h = new HashMap<String, String>(); 
     h.put("(1:" , "(30:"); 
     h.put(",1:" , ",30:"); 
     h.put("(30:" , "(6:"); 
     h.put(",30:" , ",6:"); 
     System.out.println(convertTree(tree, h)); 

    } 
    private static String convertTree(String treeOld, HashMap<String, String> conv) { 
     Pattern pattern = Pattern.compile("[,(]\\d+:"); // Init the regex 
     Matcher m = pattern.matcher(treeOld);   // Init the matcher 
     StringBuffer result = new StringBuffer();  // Declare the string buffer (can be replaced with a string builder) 
     while (m.find()) {        // Iterate through matches 
      if (conv.containsKey(m.group(0))) {   // Check if the key exists 
       m.appendReplacement(result, conv.get(m.group(0))); // If yes, use the HashMap value 
      } 
      else { 
       m.appendReplacement(result, m.group(0)); // Else, just reinsert the match value 
      } 
     } 
     m.appendTail(result);  // Append what remains to the result 
     return result.toString(); 

    } 
} 
+1

Molto più pulito del mio tentativo, grazie mille! – Darkstarone

7

capito, necessario per utilizzare un valore di offset:

private String singlePassConvert (String text, HashMap<String, String> conv) { 
     Pattern pattern = Pattern.compile("\\(\\d+:|,\\d+:"); 
     Matcher matcher = pattern.matcher(text); 
     int offset = 0; 
     while (matcher.find()) { 
      String replace = conv.get(matcher.group()); 
      String head = (String) text.subSequence(0, matcher.start() + offset); 
      String tail = (String) text.subSequence(matcher.end() + offset, text.length()); 

      text = head + conv.get(matcher.group()) + tail; 

      if (matcher.group().length() > conv.get(matcher.group()).length()) { 
       offset --; 
      } else if (matcher.group().length() < conv.get(matcher.group()).length()) { 
       offset ++; 
      } 
     } 
     return text; 

} 

Tuttavia, Fair Warning, dal momento che questa implementazione non usa StringBuilder, può essere lento su stringhe di grandi dimensioni.

Inoltre, il valore di offset funziona solo per differenze di lunghezza di +/- 1 e deve essere modificato se le differenze di lunghezza non sono note.

+0

avete risolto il problema in 5 minuti dopo la sua pubblicazione e ottenuto +5 upvotes per la questione e +4 upvotes per la risposta? Sembra abbastanza pescoso. –

+2

@krzyk E perché? Qualcuno può pubblicare una domanda immediatamente seguita da una risposta; In realtà è [incoraggiato] (http://stackoverflow.com/help/self-answer). – Maroun

+0

Sì, è possibile, ma 5 minuti dopo la pubblicazione? Con un nuovo gruppo di codice? Per me non sembra giusto, e il numero di voti positivi si aggiunge a quel brutto presentimento. –

Problemi correlati