Странное положительное поведение Lookahead в движке java.util.regex

У меня есть текст со многими животными определенных видов и некоторыми ловушками, а также другой текст без смысла, например. "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

Почему это? Я пытался привязать его к началу с добавлением ^.*? к регулярному выражению, но это не помогло.


person Arnold    schedule 15.02.2011    source источник


Ответы (4)


Вы можете сделать что-то вроде этого:

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 будет соответствовать концу.

person Bart Kiers    schedule 15.02.2011

Это потому, что ваше выражение говорит о том, что за животным не может следовать любое животное. Вот почему сопоставляется только последнее «пойманное животное».

Это сложная проблема для решения, так как в основном вы хотите сказать "с чем-то промежуточным, что не соответствует 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, а затем
  • anything character except b, c and d, or
    • b (but not followed by ird), or
    • c (но без at) или
    • d (но без og).
  • затем trap
person aioobe    schedule 15.02.2011

Я не могу редактировать, но в последней строке должно быть start = m.start + 1;.

person Arnold    schedule 15.02.2011

Как заявляет 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(), но это, вероятно, будет излишним или непрактичным в зависимости от ваших требований.

person El Ronnoco    schedule 15.02.2011