Допустим, у меня есть 10 000 регексов и одна строка, и я хочу выяснить, соответствует ли эта строка какому-либо из них, и получить все совпадения. Тривиальный способ сделать это - просто запросить строку по очереди по всем регексам. Есть ли более быстрый и эффективный способ сделать это?
EDIT: Я попробовал заменить его на DFA's (lex). Проблема в том, что это даст только один единственный шаблон. Если у меня есть строка "hello" и шаблоны "[H|h]ello" и ".{0,20}ello", то DFA будет соответствовать только одному из них, но я хочу, чтобы они оба совпали.
Я'вэ сталкивался с подобной проблемой в прошлом. Я использовал решение, аналогичное предполагается akdom.
Мне повезло в том, что мои регулярные выражения, как правило, имел какой-то подстроки, которые должны присутствовать в каждой строке он соответствует. Я был в состоянии извлечь эти подстроки, используя простой парсер и индексировать их в ФСА, используя алгоритм АХО-Корасик алгоритмов. Затем этот индекс используется для быстрого устранения всех регулярных выражений, который просто не'т соответствовать заданной строки, оставив только несколько регулярных выражений для проверки.
Я выпустила код под лицензией LGPL как модуль Python и C. См. esmre на Google код хостинг.
Мы должны были сделать это на продукт, который я когда-то работал на. Ответ был чтобы собрать вместе всех регулярных выражений в детерминированного конечного автомата (также известный как детерминированный конечный автомат или ар). Дид может быть шел посимвольно по строке и будет огонь а "Матч" в случае, когда одно из выражений соответствует.
Преимущества она работает быстро (каждый символ сравнивается только один раз) и не медленнее, если вы добавить больше выражений.
Минусы, что он требует огромный ** таблица данных для автомата, и есть много типов регулярных выражений, которые не поддерживаются (например, обратные ссылки).
Тот, который мы использовали ручной закодированный шаблон c++ гайки в нашей компании в то время, поэтому, к сожалению, я не'Т есть какие-либо решения FOSS, чтобы указать вам в сторону. Но если вы погуглите regex или регулярное выражение с "АР" Вы'll найти вещи, которые будут указывать вам в правильном направлении.
Это путь лексеров работы.
Регулярные выражения преобразуются в один не детерминированных автоматов (NFA) и которая превращается в детерминированных автоматов (DFA).
Полученный автомат будет стараться соответствовать всем регулярные выражения сразу и получится на одного из них.
Есть много инструментов, которые могут помочь вам здесь, они назвали "генератор анализатор" и есть решения, которые работают с большинством языков.
Вы Don'т сказать, какой язык вы используете. Для программистов я бы предложил посмотреть на re2c инструмент. Конечно, традиционные (Ф)Лекс - это всегда вариант.
Sulzmann Мартин сделал довольно много работы в этой области. Он а HackageDB проекта поясняет кратко здесь, которые используют производные, кажется, специально созданы для этого.
Используется язык Haskell и, таким образом, будет очень трудно переводить на функциональный язык, если это желание (я думаю, перевод на многие другие языки ФП все равно было бы довольно трудно).
Код не основан на преобразовании в серии из автоматов, а затем, комбинируя их, вместо этого он основывается на символических манипуляций самих регулярных выражений.
Также код, очень много экспериментальных и Мартин больше не профессор, но в 'трудоустройства'(1) таким образом, могут быть не заинтересованы/не в состоянии предоставить любую помощь или информацию.
10,000 regexen а? Эрик Wendelin's предложение иерархии, кажется, хорошая идея. Задумывались ли вы о снижении чудовищность этих regexen что-то вроде древовидной структуры?
В качестве простого примера: все regexen требующих ряд может ответвление одного регулярного выражения для проверки такие, все regexen не требует один вниз другой ветке. В этом моде можно уменьшить количество фактических сравнений вниз по пути вдоль дерева вместо того, чтобы делать каждое сравнение в 10,000.
Для этого потребуется демонтаж regexen на жанры, в каждом жанре имеющие общий тест, который будет править их в случае неудачи. Таким образом, вы теоретически могли бы существенно сократить количество реальных сравнений.
Если бы вам пришлось делать это во время выполнения, вы могли бы разобрать ваш приведенный регулярные выражения и "файл" их в предварительно определенных жанров (проще всего сделать) или сравнительная жанров, составленные в тот момент (не так легко сделать).
Ваш пример сравнения "Привет", чтобы "[Ч|ч]превед" и ".Элло {0,20}на" выиграл'т действительно помогло это решение. Простой случай, где это может быть полезно будет: если у тебя 1000 тестов, которые будут возвращать только true, если на "превед", которая существует где-то в строке, а тестовая строка "прощай;" Вы только должны сделать один тест на "Привет" и знаю, что 1000 тестов, требующих его выиграл'т работу, и из-за этого, ты выиграл'т иметь, чтобы сделать их.
Если вы'вновь мышление в терминах "и что quot 10,000 выражений&; вам надо перенести ваш хоть процессы. Если ничего другого, думать о том, что "10,000 целевой строки в матче с". Потом искать номера-регулярное выражение методики, построенные для борьбы с "вагоны целевой строки" в ситуациях, таких как АХО-Корасик машины. Хотя, если честно, кажется, что-то сошла с рельсов на более ранних этапах, чем машины, с 10,000 строках звучит намного больше, как поиск по базе данных, чем совпадение строки.
Необходимо каким-то образом определить, является ли данный regex "аддитивным" по отношению к другому. Создание своеобразной "иерархии" регексов, позволяющей определить, что все регексы определенной ветви не совпадают
Вы могли бы объединить их в группы, может быть, 20.
(?=(regex1)?)(?=(regex2)?)(?=(regex3)?)...(?=(regex20)?)
До тех пор, пока выражение имеет нулевую (или как минимум такое же количество) групп захвата, вы можете посмотреть на то, что захватили, чтобы увидеть, какой рисунок(ы) соответствует.
Если regex1 совпал, группа захвата 1 было бы это'ы найденного текста. Если нет, это будет не определено
/нет
/нуль
/...
АХО-Корасик был ответ для меня.
Я имел категории 2000 вещей, которые каждый составил список моделей на матч против. Длина струн в среднем около 100 000 знаков.
Основная проблема: в скороговорки на матч были все языковые паттерны не шаблона regex, например, 'кошечка'
против Р'\ш+'
.
Я использовал питон и так привык https://pypi.python.org/pypi/pyahocorasick/.
import ahocorasick
A = ahocorasick.Automaton()
patterns = [
[['cat','dog'],'mammals'],
[['bass','tuna','trout'],'fish'],
[['toad','crocodile'],'amphibians'],
]
for row in patterns:
vals = row[0]
for val in vals:
A.add_word(val, (row[1], val))
A.make_automaton()
_string = 'tom loves lions tigers cats and bass'
def test():
vals = []
for item in A.iter(_string):
vals.append(item)
return vals
Запуск %раз все проверить () на мой категориях 2000 с 2-3 следами по категориям и _string
длина примерно 100,000меня
2.09 мспротив
631 МСпоследовательной
повторно.поиск()` 315x быстрее!.
Если вы're, используя настоящие регулярные выражения (те, которые соответствуют регулярные языки с формальной языковой теории, а не какой-Perl-подобные номера-обычное дело), то вы'вновь повезло, потому что регулярные языки замкнуты в рамках Союза. В большинстве регулярных выражений языка, труба ( | ) - это союз. Поэтому вы должны быть в состоянии построить строку (представляющую собой регулярное выражение, которое вы хотите) следующим образом:
(r1)|(r2)|(r3)|...|(r10000)
где круглые скобки используются для группировки значений, не совпадающих. Все, что соответствует этому регулярному выражению соответствует хотя бы одному из ваших оригинальных регулярные выражения.
Я'd не сказать, что это'ы работа для настоящего разбора. Медиана может быть разбор грамматики выражений (ПЭГ). Это'ы более высокого уровня абстракции по шаблону, одной особенностью является то, что вы можете определить всю грамматику, а не единый шаблон. Есть несколько высокопроизводительных реализациях, которые работают по компиляции грамматики в байт-код и запуск его в специализированной виртуальной машины.
отказ от ответственности: я знаю только одного-это LPEG, библиотека Луа, и это было'т легкая (для меня), чтобы понять базовые понятия.
Вы можете компилировать регулярное выражение на гибрид ДФА/Букши автоматов, где каждый раз БА вводит принимает государственный флаг тебе какое регулярное выражение правило на "Хит".
Букши-это немного перебор, но изменяя способ ваш ДФА работы может сделать трюк.
Я бы порекомендовал с помощью Intel'ы Hyperscan если все, что вам нужно-это знать, что регулярные выражения совпадают. Он построен для этой цели. Если действия, которые вы должны принять несколько более сложный, можно также использовать Рагель. Хотя он производит один DFA и может привести во многих государствах, и, следовательно, очень большие исполняемой программы. Hyperscan занимает гибридный НКА/ДКА/пользовательский подход к сопоставлению, который обрабатывает большое количество выражений хорошо.
Я бы почти предложил написать "внутренний"- regex-движок - такой, где 'цель' - regex, а 'термин' - строка.
Однако, похоже, что ваше решение, заключающееся в итеративном переборе каждого из них, будет гораздо проще.
Я использую Рагель с отъездом действия:
action hello {...}
action ello {...}
action ello2 {...}
main := /[Hh]ello/ % hello |
/.+ello/ % ello |
any{0,20} "ello" % ello2 ;
Строка "Привет" называете код в блоке действия привет, потом в
заблокировать действие Элло` и, наконец, в блок ello2 действия.
Их регулярные выражения весьма ограничены, а язык машины предпочтительнее, брекеты из вашего примера только работать с более общими язык.
Я думаю, что короткий ответ да, есть способ сделать это, и что это хорошо известно информатики, и, что я могу'т помню, что это такое.
Короткий ответ заключается в том, что вы можете обнаружить, что ваш интерпретатор регулярных выражений уже занимается всем этим эффективно, когда |'d вместе, или вы можете найти один, что делает. Если нет, он's время для вас, чтобы погуглить сравнения строк и алгоритмы поиска.
Самый быстрый способ сделать это, кажется, что-то вроде этого (код на C#):
public static List<Regex> FindAllMatches(string s, List<Regex> regexes)
{
List<Regex> matches = new List<Regex>();
foreach (Regex r in regexes)
{
if (r.IsMatch(string))
{
matches.Add(r);
}
}
return matches;
}
Ах, вы имели в виду самый быстрый код? я не'т знаю тогда....