Как это работает?
Взгляните на теорию автоматов
Короче говоря, каждое регулярное выражение имеет эквивалентный конечный автомат и может быть скомпилировано и оптимизировано до конечного автомата. Соответствующие алгоритмы можно найти во многих сборниках. Эти алгоритмы используются программами 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.