2009-10-23 97 views
2

我正尝试在Java中为replaceAll方法创建一个正则表达式。测试字符串是abXYabcXYZ,模式是abc。我想用+替换除图案以外的任何符号。例如,字符串abXYabcXYZ和模式[^(abc)]应返回++++abc+++,但在我的情况下它返回ab++abc+++Java中的正则表达式问题

public static String plusOut(String str, String pattern) { 
    pattern= "[^("+pattern+")]" + "".toLowerCase(); 
    return str.toLowerCase().replaceAll(pattern, "+"); 
} 
public static void main(String[] args) { 
    String text = "abXYabcXYZ"; 
    String pattern = "abc"; 
    System.out.println(plusOut(text, pattern)); 
} 

当我尝试替换+模式是没有问题的 - abXYabcXYZ与模式(abc)返回abxy+xyz。模式(^(abc))返回没有替换的字符串。

有没有其他的方式来写NOT(正则表达式)或组符号作为一个单词?

回答

11

由于没有办法表达“替换不匹配模式的字符串”,所以您试图实现的功能对于正则表达式来说非常困难。你将不得不使用“正面”模式,告诉匹配什么,而不是不匹配什么。

此外,您想用替换字符替换每个字符,因此您必须确保您的模式完全匹配一个字符。否则,您将用一个字符替换整个字符串,返回一个较短的字符串。

对于你的玩具的例子,你可以使用负面的lookaheads和lookbeheads来实现这个任务,但是对于具有更长或更复杂的字符串的现实世界的例子来说这可能更困难,因为你必须考虑你的字符串的每个字符另外还有其背景。

这里是“不 'ABC'”的图案:

[^abc]|a(?!bc)|(?<!a)b|b(?!c)|(?<!ab)c 

它由五个子图案,以连接“或”(|),每个匹配的一个字符:

  • [^abc]除了abc
  • a(?!bc)比赛a如果每一个字符匹配它后面没有bc
  • (?<!a)b匹配b如果它不与a
  • b(?!c)匹配b之前,如果它不跟c
  • (?<!ab)c匹配c如果它不与ab
之前

这个想法是匹配不在你的目标词abc中的每个字符,加上根据上下文的每个单词字符,不属于你的话。可以使用负向预测(?!...)和向后看(?<!...)来检查上下文。

你可以想象一旦你有一个目标字包含一个字符不止一次,如example,这种技术将失败。如果不匹配x之前没有l“,匹配e是相当困难的。

特别是对于动态模式,执行正面搜索然后替换第二遍中不匹配的每个字符(如其他人在此处所建议的那样)会更容易。

+1

很好的解释。 +1 – jensgram 2009-10-23 08:37:38

1

[^ ...]将匹配一个字符不是任何的...

所以你的模式 “[^(ABC)” 是说“匹配一个字符不能是A,B, c或左或右括号“;事实上,这就是你的测试中发生的情况。

很难说“在一个简单的正则表达式中替换所有不属于字符串'abc'的字符。你可能会做什么,而不是达到你想要也能像

while the input string still contains "abc" 
    find the next occurrence of "abc" 
    append to the output a string containing as many "+"s as there are characters before the "abc" 
    append "abc" to the output string 
    skip, in the input string, to a position just after the "abc" found 
append to the output a string containing as many "+"s as there are characters left in the input 

或一些讨厌的事情是什么可能如果输入字母表受到限制,你可以使用正则表达式做这样的事情

replace all occurrences of "abc" with a single character that does not occur anywhere in the existing string 
replace all other characters with "+" 
replace all occurrences of the target character with "abc" 

这将是更具可读性但性能可能不佳

+0

是啊,这就是我会做。但试图用正则表达式来做是一个很好的谜题。 – Thilo 2009-10-23 07:47:25

0

否定regexps通常很麻烦。我认为你可能想要使用负面预测。像这样的东西可能会工作:

String pattern = "(?<!ab).(?!abc)"; 

我没有测试它,所以它可能不适用于退化情况。表现可能也很糟糕。使用多步算法可能会更好。

编辑:不,我认为这不适用于每种情况。你可能会花更多的时间来调试这样的正则表达式,而不是用一些额外的代码来算法化。

0

设法解决它没有正则表达式:与其

String out = ""; 
int i; 
for(i=0; i<text.length() - pattern.length() + 1;) { 
    if (text.substring(i, i + pattern.length()).equals(pattern)) { 
     out += pattern; 
     i += pattern.length(); 
    } 
    else { 
     out += "+"; 
     i++; 
    } 
} 
for(; i<text.length(); i++) { 
    out += "+"; 
} 
0

不是单一的replaceAll,你总是可以尝试类似:如果不使用replaceAll(...)

​​
0

,我会去的Pattern/Matcher的方法:

import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

public class Main { 

    public static String plusOut(String str, String pattern) { 
     StringBuilder builder = new StringBuilder(); 
     String regex = String.format("((?:(?!%s).)++)|%s", pattern, pattern); 
     Matcher m = Pattern.compile(regex).matcher(str.toLowerCase()); 
     while(m.find()) { 
      builder.append(m.group(1) == null ? pattern : m.group().replaceAll(".", "+")); 
     } 
     return builder.toString(); 
    } 

    public static void main(String[] args) { 
     String text = "abXYabcXYZ"; 
     String pattern = "abc"; 
     System.out.println(plusOut(text, pattern)); 
    } 

} 

请注意,你会如果您的String pattern包含正则表达式元字符,则需要使用Pattern.quote(...)

编辑:我没有看到一个Pattern/Matcher的方法已经由toolkit(虽然略有不同)建议......