Как на самом деле работают регулярные выражения?

30

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

Является ли использование регулярного выражения быстрее, чем разбор файла строка за строкой и слово за словом в поисках совпадения? Если так, как это работает? Как ты можешь идти быстрее, чем смотреть на каждое слово?

Lazer
источник
5
Вы предполагаете (имея в виду ноль доказательств), что регулярное выражение будет быстрее, но вы не знаете, почему это так? Может быть, вы должны пересмотреть свое предположение тогда.
фунтовые
3
Итак, предположение. если бы у меня были доказательства, это было бы не так, верно?
LazeR
4
Не в этом дело. Дело в том, что привело вас к этому предположению ... Вам не нужны доказательства для ваших вопросов, но вам нужны аргументы для ваших предположений.
Яннис
1
не каждый символ входной строки просто перемещает конечный автомат в следующее состояние. Я не понимаю, как кто-то может сделать эту операцию медленной ...
tp1
2
Я не уверен, что быстрее, но моя основная причина использования регулярных выражений заключается в элегантности сложных шаблонов сопоставления, просто вы не найдете лучшего способа сформулировать это в среде программирования.
Манторок

Ответы:

47

Как это работает?

Взгляните на теорию автоматов

Короче говоря, каждое регулярное выражение имеет эквивалентный конечный автомат и может быть скомпилировано и оптимизировано до конечного автомата. Соответствующие алгоритмы можно найти во многих сборниках. Эти алгоритмы используются программами Unix, такими как awk и grep.

Однако большинство современных языков программирования (Perl, Python, Ruby, Java (и языки на основе JVM), C #) не используют этот подход. Они используют рекурсивный подход обратного отслеживания, который компилирует регулярное выражение в дерево или последовательность конструкций, представляющих различные подчасти регулярного выражения. Большинство современных синтаксисов «регулярных выражений» предлагают обратные ссылки, которые находятся за пределами группы регулярных языков (они не имеют представления в конечных автоматах), которые тривиально реализуются в рекурсивном подходе обратного отслеживания.

Оптимизация обычно дает более эффективный конечный автомат. Например: рассмотрим aaaab | aaaac | aaaad, обычный программист может получить простую, но менее эффективную реализацию поиска (сравнивая три строки отдельно) прямо за десять минут; но, понимая, что это эквивалентно aaaa [bcd], лучший поиск можно выполнить, выполнив поиск первых четырех «a», а затем проверив 5-й символ по [b, c, d]. Процесс оптимизации был одним из моих домашних заданий компилятора много лет назад, поэтому я предполагаю, что он также используется в большинстве современных движков регулярных выражений.

С другой стороны, конечные автоматы имеют некоторое преимущество, когда они принимают строки, потому что они используют больше места по сравнению с «тривиальной реализацией». Рассмотрим программу для отмены кавычек в строках SQL, а именно: 1) начинается и заканчивается одинарными кавычками; 2) одинарные кавычки экранируются двумя последовательными одинарными кавычками. Итак: input ['a' ''] должен давать output [a ']. С помощью конечного автомата последовательные одинарные кавычки обрабатываются двумя состояниями. Эти два состояния служат для запоминания истории ввода так, что каждый входной символ обрабатывается ровно только один раз, как показано ниже:

...
S1->'->S2
S1->*->S1, output *, * can be any other character 
S2->'->S1, output '
S2->*->END, end the current string

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

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

Конкретный движок из фреймворка / библиотеки может быть медленным, потому что движок делает кучу других вещей, которые программисту обычно не нужны. Пример: класс Regex в .NET создает несколько объектов, включая Match, Groups и Captures.

Codism
источник
2
Я не мог бы сказать это лучше сам. Единственное, что я хотел бы добавить: регулярные выражения также могут восполнить ленивых программистов. В примере вы упомянули aaaab|aaaac|aaaadпротив aaaa[bcd]. Стоит явно указать, что эти два математически эквивалентны и создают один и тот же DFA, что дает программистам больше свободы для представления регулярного выражения таким образом, который имеет смысл (не то, что это обычная практика, но ... знаете). ..
прогулка
Спасибо, это действительно имело смысл благодаря классу автоматов, который я взял
lazeR
Является ли это примером тривиальной проблемы, когда регулярные выражения избыточны ?: stackoverflow.com/questions/18955099/…
Менелай Бакопулос
17

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

Еще в 1980-х годах, когда 1 MIPS был быстрым компьютером, регулярные выражения были довольно большой областью беспокойства, беспокойства и исследований, потому что они были медленными, уродливыми и требовали большого количества вычислений. Разумная разработка алгоритмов последовала и помогла - но для всех практических целей в наши дни вы видите чудо быстрых машин, покрывающих трещины.

quickly_now
источник
2
Если вы просто ищете одно слово, оба метода одинаковы (или регулярное выражение немного медленнее). Но, учитывая сложное выражение (и текст достаточно большого размера), регулярное выражение, вероятно, будет быстрее, чем простой поиск (при условии, что вы пишете простой поиск (вы всегда можете написать сложный поиск, который будет таким же быстрым)). Теперь, когда погода важна, это слишком общий вопрос, и вам придется рассматривать его в каждом конкретном случае.
Мартин Йорк
3
-1. Теория регулярных выражений восходит к 50-м годам и сыграла важную роль в создании лексических анализаторов (и, соответственно, компиляторов). Они создают очень эффективные автоматы, которые (доказуемо) используют наименьшее число возможных состояний. Получающиеся конечные автоматы могут сопоставлять сложные шаблоны гораздо быстрее, чем все, что вы могли бы написать вручную. Они выглядят быстро, потому что они быстры.
Прогулка
Возможно, немного упустил мою точку зрения. Они могут быть «быстрыми», но это все относительно - предстоит еще много работы. Некоторые из других ответов здесь также читают.
quick_now
Этот ответ имеет отношение к вопросу? а как 13 голосов?
Садананд
7

Почему вы думаете, что они быстрее, чем поиск документа?

Есть несколько трюков, которые вы можете сделать, например. если вы ищете 10-буквенное слово, начинающееся с A и заканчивающееся на B, тогда, если вы найдете A, а позиция 9 далее - это не B, вы можете пропустить некоторые из них. см. алгоритм Кнута – Морриса – Пратта

Мартин Беккет
источник
5

Что делает регулярное выражение быстрым?

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

Они также не подходящий инструмент для каждой работы - молоток .

ладья
источник
+1 Спасибо за напоминание мне об этом конкретном произведении искусства ...
Яннис
5

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

GrandmasterB
источник
4
s / squeak / squeeze /?
Петер Тёрёк
4

Ваша основная предпосылка неверна.

Регулярные выражения не всегда быстрее простого поиска. Все зависит от контекста. Это зависит от сложности выражения, длины искомого документа и множества факторов.

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

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

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

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

Мартин Йорк
источник
1

Вполне вероятно, что в некоторых языках высокого уровня (возможно, javascript) использование библиотеки регулярных выражений, реализованной на языке низкого уровня (возможно, C), будет быстрее, чем написание логики синтаксического анализатора на языке высокого уровня.

Вероятно - я понятия не имею, так ли это на самом деле.

Стив Беннетт
источник
Хороший! Это то, что я тоже рассмотрел. Но поскольку современные процессоры работают быстрее, чем их предшественники, я могу с уверенностью сказать, что если вы будете эффективно писать код, вы редко сможете увидеть разницу. Я на самом деле в целом не очень разгуливаю над всей гипотезой ускорения регулярного выражения! ;-)
user3833732