1

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

我正在尝试构建一个正则表达式,它将找到每个陷阱的最近优先级动物,例如“cat dog house 131 bird 1341 house trap cat cat cat dog trap house dog house trap ”。

我写了这个正则表达式:(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

4 回答 4

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匹配作为结尾。

于 2011-02-15T13:15:27.577 回答
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, 其次是
  • 除了b,cd, or 之外的任何字符
    • b(但后面不跟ird),或
    • c(但后面不跟at),或
    • d(但后面没有og)。
  • 其次是trap
于 2011-02-15T12:06:20.477 回答
0

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

于 2011-02-15T11:50:44.603 回答
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()但根据您的要求,这可能是矫枉过正或不切实际。

于 2011-02-15T13:09:04.200 回答