【问题标题】:Weird Positive Lookahead behaviour in java.util.regex enginejava.util.regex 引擎中奇怪的正向前瞻行为
【发布时间】:2011-02-15 11:47:34
【问题描述】:

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

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

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

这是为什么呢?我试图通过在正则表达式前面加上 ^.*? 来将其锚定到开头,但它没有帮助。

【问题讨论】:

    标签: java regex lookahead


    【解决方案1】:

    你可以这样做:

    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 的第一次出现被匹配为结尾。

    【讨论】:

      【解决方案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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          【解决方案4】:

          正如 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(),但根据您的要求,这可能是矫枉过正或不切实际。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-05-07
            • 2021-12-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多