2016-05-15 39 views
11

我正試圖在newick格式樹上轉換節點,並且無法獲得替換權限。說我有HashMap一次傳遞Java多個替換

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

和樹:

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

傳統智慧會建議多replaceAll,但這帶來了一個問題:

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

這裏的問題是我們」已經取代了之前被替換的節點。正確的樹應該是這樣的:

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

現在我已經做到了這一點在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) 

但我有我的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; 

    } 

雖然替換似乎工作,我不能使用不同大小的字符串正確索引(如示例中所示)。有沒有辦法在Java中做到這一點?

回答

8

您可以使用Matcher#appendReplacement在匹配時修改字符串。

注意,作爲選擇分支只在第一個字符不同您正則表達式可以簡化爲[,(]\d+:[,(]匹配任一,()。

這裏是一個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

比我的嘗試更清潔,非常感謝! – Darkstarone

7

想通了,要使用的偏移值所需要的:

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; 

} 

然而,公平的警告,因爲這實現不使用StringBuilder,也可能是大串緩慢。

此外,偏移值僅適用於+/- 1長度差異,如果長度差異未知,應該修改。

+0

你在5分鐘解決了這個問題發佈之後,得到了+5 upvotes的提問和+4 upvotes的答案?看起來很腥。 –

+2

@krzyk爲什麼呢?有人可以立即發佈問題,然後回答問題;它實際上[鼓勵](http://stackoverflow.com/help/self-answer)。 – Maroun

+0

是的,這可能,但發佈後5分鐘?用一堆新的代碼?對我來說,它看起來不對,而贊助人數量增加了這種不好的感覺。 –