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

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

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

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

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

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

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

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

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

дополнительные выводы-используя библиотеку автоматов выше, проблема сводится к следующему: если вы объединяете два или более автоматов, как вы можете определить для конечного состояния, которому из исходных автоматов соответствует?

3 ответов


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


я реализовал такое решение на основе dk.Брикс.автомата, вы можете найти его здесь. https://github.com/fulmicoton/multiregexp


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

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

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

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

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

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

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

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