2016-05-15 68 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分钟?用一堆新的代码?对我来说,它看起来不对,而赞助人数量增加了这种不好的感觉。 –

相关问题