Как я могу выполнить частичное соответствие с java.util.regex.*?

Я использовал java.util.regex.* классы для Регулярного выражения в Java и всей пользе до сих пор. Но сегодня у меня есть другое требование. Например, полагайте, что шаблон "aabb". Теперь, если входная строка будет aa, то это не будет определенно соответствовать, однако существует все еще возможность, что, если я добавляю bb, это становится aabb, и это соответствует. Однако, если я запустил бы с cc, независимо от того, что я добавляю его, никогда не будет соответствовать.

Я исследовал класс Pattern и Matcher, но не нашел способа достигнуть этого.

Вход прибудет от пользователя, и система должны ожидать, пока соответствия шаблона или это никогда не будут соответствовать независимо ни от какого входа далее.

Какая-либо подсказка?

Спасибо.

25
задан Alan Moore 18 March 2010 в 14:15
поделиться

6 ответов

Один из способов сделать это - разобрать ваше регулярное выражение на последовательность подрегулярных выражений, а затем собрать их таким образом, чтобы вы могли выполнять частичные совпадения; например «ab c» имеет 3 подрегегекса «a», «b » и «c», которые затем можно собрать как «a (b * (c)?)?».

Ситуация усложняется, когда входное регулярное выражение содержит чередование и группы, но должен работать тот же общий подход.

Проблема с этим подходом состоит в том, что результирующее регулярное выражение является более сложным и потенциально может привести к чрезмерному отслеживанию с возвратом для сложных входных регулярных выражений.

0
ответ дан 28 November 2019 в 20:53
поделиться

Итак, вы хотите знать, не совпадает ли String s с регулярным выражением, а может ли совпадать более длинная строка, начинающаяся с s? Извините, регулярные выражения не могут вам помочь, потому что у вас нет доступа к внутреннему состоянию сопоставителя; вы получаете только логический результат и любые группы, которые вы определили, поэтому вы никогда не узнаете , почему совпадение не удалось.

Если вы хотите взломать библиотеки JDK, вы можете расширить (или, возможно, разветвить) java.util.regex и предоставить дополнительную информацию о процессе сопоставления. Если совпадение не удалось, потому что вход был «израсходован», ответ будет истина ; если он не удался из-за распознавания символов или других проверок, он будет ложным . Это кажется большой работой, потому что ваша проблема полностью противоположна тому, что должны делать регулярные выражения.

Другой вариант: может быть, вы можете просто переопределить задачу, чтобы обрабатывать ввод как регулярное выражение и сопоставлять aabb с * aa. **? Однако вы должны быть осторожны с метасимволами регулярных выражений.

1
ответ дан 28 November 2019 в 20:53
поделиться

Если вы сделаете каждый символ регулярного выражения необязательным и уменьшите множественность ограничения, вы как бы получаете то, что хотите. Пример: если у вас есть соответствующий шаблон «aa (abc) + bbbb», у вас может быть шаблон «возможное совпадение» 'a? A? (A? B? C?) * B? B? B? B?'.

Этот механический способ создания шаблона возможного совпадения не распространяется на сложные конструкции, такие как прямые и обратные ссылки.

0
ответ дан 28 November 2019 в 20:53
поделиться

В приведенном вами примере вы можете попытаться использовать анти-шаблон для дисквалификации недействительных результатов. Например, «^ [^ a]» сообщит вам, что вы вводите «c ...» не может соответствовать вашему примеру шаблона «aabb».

В зависимости от вашего шаблона вы можете разбить его на более мелкие шаблоны, чтобы проверить и использовать несколько сопоставлений, а затем установить их границы, когда происходит одно совпадение, и вы переходите к следующему. Этот подход может работать, но если ваш шаблон сложен и может иметь подчасти переменной длины, вы можете в конечном итоге переопределить часть сопоставления в своем собственном коде, чтобы настроить возможные границы сопоставления, чтобы сделать его более или менее жадным. Общая идея псевдокода для этого будет следующая:

boolean match(String input, Matcher[] subpatterns, int matchStart, int matchEnd){
  matcher = next matcher in list;
  int stop = matchend;
  while(true){
    if matcher.matches input from matchstart -> matchend{
      if match(input, subpatterns, end of current match, end of string){
        return true;
      }else{
        //make this match less greedy
        stop--;
      }
    }else{
      //no match
      return false;
    }
  }
}

Затем вы могли бы объединить эту идею с анти-шаблонами и иметь анти-подшаблоны, и после каждого совпадения подшаблонов вы проверяете следующий анти-шаблон, если он соответствует, вы знаете, что не удалось, в противном случае продолжить поиск по шаблону. Вы, вероятно, захотите вернуть что-то вроде перечисления вместо логического (например, ALL_MATCHED, PARTIAL_MATCH, ANTI_PATTERN_MATCH, ...)

Опять же, в зависимости от сложности вашего фактического шаблона, который вы пытаетесь сопоставить, написав соответствующие подшаблоны / анти-шаблон может быть трудным, если не невозможным.

0
ответ дан 28 November 2019 в 20:53
поделиться

Вы могли бы сделать это с помощью конечного автомата ( http://en.wikipedia.org/wiki/State_machine ) . Ваши состояния / переходы должны представлять действительный ввод и одно состояние ошибки. Затем вы можете передать в конечный автомат по одному символу (возможно, подстроке в зависимости от ваших данных) за раз. В любой момент вы можете проверить, находится ли ваш конечный автомат в состоянии ошибки. Если он не в состоянии ошибки, значит, вы знаете, что будущий ввод все еще может совпадать. Если он находится в состоянии ошибки, значит, вы знаете, что что-то ранее не удалось, и любой будущий ввод не сделает строку действительной.

-1
ответ дан 28 November 2019 в 20:53
поделиться

Вам следовало бы более внимательно изучить API-интерфейс Matcher; метод hitEnd () работает точно так, как вы описали:

import java.util.regex.*;

public class Test
{
  public static void main(String[] args) throws Exception
  {
    String[] ss = { "aabb", "aa", "cc", "aac" };
    Pattern p = Pattern.compile("aabb");
    Matcher m = p.matcher("");

    for (String s : ss) {
      m.reset(s);
      if (m.matches()) {
        System.out.printf("%-4s : match%n", s);
      }
      else if (m.hitEnd()) {
        System.out.printf("%-4s : partial match%n", s);
      }
      else {
        System.out.printf("%-4s : no match%n", s);
      }
    }
  }
}

output:

aabb : match
aa   : partial match
cc   : no match
aac  : no match

Насколько мне известно, Java - единственный язык, который предоставляет эту функциональность.Также существует метод requireEnd () , который сообщает вам, может ли дополнительный ввод превратить совпадение в несоответствие, но я не думаю, что это актуально в вашем случае.

Оба метода были добавлены для поддержки класса Scanner, поэтому он может применять регулярные выражения к потоку, не требуя считывания всего потока в память.

36
ответ дан 28 November 2019 в 20:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: