Подтвердить что ты не робот

Объединение нескольких регулярных выражений в один автомат

Скажем, что у меня есть список регулярных выражений (читается из внешнего источника - файла, базы данных и т.д.). Я хочу проверить, какое из этих регулярных выражений соответствует строке.

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

Я могу объединить все эти регулярные выражения в один (с | между ними), но тогда проблема в том, что я могу идентифицировать только первое согласованное регулярное выражение, не все.

Еще одна идея могла бы заключаться в создании автомата для всех этих регулярных выражений и для обозначения конечных состояний, например, с помощью указателей соответствующего регулярного выражения. Я смотрел http://cs.au.dk/~amoeller/automaton/, библиотеку, которая кажется способной работать с регулярными выражениями и автоматом, но не уверена, что ее можно расширить, чтобы решить мою проблему.

Есть ли у вас другие идеи?

Чтобы прояснить некоторые комментарии, я добавил пример кода:

import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class PatternTest {
    public static void main(String[] args) {
        Pattern p = Pattern.compile("(a(?:b|c)a)|((?:a|b)ba)|(ab(?:a|c))");     
        Matcher m = p.matcher("aba");
        System.out.println(m.matches());
        System.out.println(m.groupCount());
        for (int i = 0, n = m.groupCount(); i < n; i++) {
            System.out.println(m.group(i));
        }
    }
}

распечатает

true
3
aba
aba
null

Как вы видите, только первая группа сопоставлена, и я не вижу способа сопоставления двух других.

Дополнительные выводы. Используя вышеприведенную библиотеку автомата, проблема сводится к следующему: если вы объединяете два или более автомата, как вы можете идентифицировать конечное состояние, с которым соответствует исходный автомат?

4b9b3361

Ответ 1

dk.brics.automaton не поддерживает это напрямую, но вы можете обобщить представление автоматов (и соответствующих операций с автоматами), чтобы отличить между различными видами состояний принятия. Начните с добавления поля int, например, к классу State и используйте это поле всякий раз, когда устанавливается "accept".

Ответ 3

Для окончательного ответа (если он есть) нам понадобится дополнительная информация, например:

  • Вы говорите, что список регулярных выражений огромен; Вы можете быть более конкретным? Тысячи? Миллионы? Миллиарды и миллиарды?

  • Кто писал эти регулярные выражения и знает, что они делают? Являются ли регулярные выражения тщательно проверенными (для корректности и) перед добавлением в список?

  • В вашем примере кода вы используете метод matches(), который требует, чтобы regex описывал всю строку. Он действует так, как если бы регулярное выражение действительно было \A(?:(a(?:b|c)a)|((?:a|b)ba)|(ab(?:a|c)))\z
    который соответствует "aba", но не "aaba" или "abaa". Если вы использовали регулярные выражения в других инструментах или языках перед тем, как приступить к Java, это может вас удивить. Традиционно строка всегда говорила, что она "соответствует" регулярному выражению, если регулярное выражение описывает любую подстроку в строке, даже подстроку нулевой длины. Чтобы получить это поведение на Java, вы должны использовать метод Matcher find().

  • Есть ли какие-либо общие факторы, которые вы можете вытащить из всех регулярных выражений в списке, таких как минимальная или максимальная длина, общие подстроки или общие подмножества символов? Например, любая строка, соответствующая одному из ваших образцов, должна также соответствовать [abc]{3}. Если есть, возможно, вы захотите создать на их основе фильтры (возможно, регулярное выражение, возможно, нет), прежде чем начнется серьезное сопоставление. (Я бы не предлагал этого, если бы вы использовали Perl, который является choc-a-bloc с такими оптимизациями, как это уже, но Java не слишком гордится тем, что может немного помочь. ☺)

Но я чувствую себя довольно уверенно, советуя вам идти с отдельными регулярными выражениями, а не конкатенировать их всех в один. Frankenregex не обязательно будет работать лучше, и устранение неполадок было бы кошмаром! Вы можете предварительно скомпилировать все объекты Pattern, и вы можете создать объект Matcher раньше времени и повторно использовать его для всех совпадений, например:

m.reset(s).usePattern(p);

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