java.util.regex引擎中的奇怪的正向前瞻行为

时间:2011-02-15 11:47:35

标签: java regex lookahead

我发了许多种类和一些陷阱的动物的文字,以及没有意思的其他文字,例如: “cat dog house 131 bird 1341 house trap cat cat cat dog trap house dog house trap”。

我正在尝试构建一个正则表达式,它将为每个陷阱找到最接近的优先级动物,例如“猫狗屋131 1341屋 陷阱 猫猫猫 陷阱 房子房子 陷阱 “。

我写过这个正则表达式:(cat|dog|bird)(?!.*(cat|dog|bird).*).*trap

这是我的完整Java代码:

Pattern p = Pattern.compile("(cat|dog|bird)(?!.*(cat|dog|bird).*).*trap");
Matcher m = p.matcher("cat dog house 131 bird 1341 house trap cat cat cat dog trap house dog house trap");
int start = 0;
while (m.find(start)) {
    System.out.println(m.group(0));
    System.out.println(m.group(1));
    start = m.start + 1; //increment
}

很奇怪它只发现了最后一次骚扰,而不是第一次,第二次和最后一次。上面代码的输出是:

dog house trap
dog

为什么?我试图将^.*?添加到正则表达式之前将其锚定到开头,但它没有帮助。

4 个答案:

答案 0 :(得分:3)

您可以这样做:

Pattern p = Pattern.compile("(cat|dog|bird)((?!cat|dog|bird).)*?trap");
Matcher m = p.matcher("cat dog house 131 bird 1341 house trap cat cat cat dog trap house dog house trap");
while (m.find()) {
  System.out.println(m.group(1) + " :: " + m.group(0));
}

产生:

bird :: bird 1341 house trap 
dog :: dog trap 
dog :: dog house trap

简要说明:

(cat|dog|bird)        # match one of: 'cat', 'dog' or 'bird'
(                     # start group 2
  (?!cat|dog|bird).   #  if none of 'cat', 'dog' or 'bird' are ahead, match any char (except line breaks)
)*?                   # end group 2 and reluctantly match it zero or more times
trap                  # match 'trap'

可以在负面预测中添加trap,但“不情愿”的量词将导致第一次出现trap作为结束。< / p>

答案 1 :(得分:2)

这是因为你的表达说动物不能被任何动物所追随。这就是为什么只有最后一只“被困动物”匹配的原因。

这是一个难以解决的问题,因为基本上你想说“之间的某些内容不匹配 bird|dog|cat ”。

我能想到的最好的解决方案就是这个(它并不漂亮!)

import java.util.regex.*;

public class Test {

    public static void main(String[] args) {

        String pat = "(cat|dog|bird)([^bcd]|b(?!ird)|c(?!at)|d(?!og))*trap";

        String str = "cat dog house 131 bird 1341 house trap cat cat cat dog " +
                     "trap house dog house trap";

        Pattern p = Pattern.compile(pat);
        Matcher m = p.matcher(str);
        int start = 0;
        while (m.find(start)) {
            System.out.printf("Found trapped %s at %d%n", m.group(1), m.start());
            start = m.start() + 1;
        }
    }
}

<强>输出:

Found trapped bird at 18
Found trapped dog at 51
Found trapped dog at 66

基本上它说,

  • cat|dog|bird,然后是
  • bcd之外的任何字符,或
    • b(但未跟ird)或
    • c(但未跟at)或
    • d(但未跟og)。
  • 后跟trap

答案 2 :(得分:0)

我无法编辑,但在最后一行应该是start = m.start + 1;

答案 3 :(得分:0)

正如aioobe所说,解决这个问题很麻烦,只有当您的要求变得更加复杂时,才能通过RegEx解决问题。

(伪代码)的某些内容......

str = "cat dog house 131 bird 1341 house trap cat cat cat dog trap house dog house trap";
arr = str.split(" "); //split on spaces
trapping = null;

for each item in arr {
  if (isTrap(item) && trapping != null) { 
    reportTrappedAnimal(trapping);
    trapping = null;     
  } else if (isAnimal(item)) { 
    trapping = item;
  }
}

您可以使用正则表达式来实现isAnimal()isTrap(),但根据您的要求,这可能是过度或不切实际的。