Логическое И с регулярными выражениями с множественными утверждениями можно значительно ускорить, привязав их к началу строки. Или, что еще лучше, используйте два регулярных выражения: одно для списка OR
ed с использованием метода re.search
, а второе регулярное выражение с логическим списком AND
ed с использованием метода re.match
, например:
def boolean_and_new(terms):
return ''.join([r'(?=.*?\b%s\b)' % (term) for term in terms])
def run_new():
words_and_phrases = ['apple', 'cherry pie']
booleans = [boolean_and_new(terms) for terms in [
['sweet pie', 'savoury pie', 'meringue'],
['chicken pie', 'beef pie']]]
regex1 = re.compile(r'(?i)\b(?:%s)\b' % ('|'.join(words_and_phrases)))
regex2 = re.compile(r'(?i)%s' % ('|'.join(booleans)))
matched_data = list()
for d in data():
if regex1.search(d) or regex2.match(d):
matched_data.append(d)
Эффективные регулярные выражения для этого набора данных:
regex1 = r'(?i)\b(?:apple|cherry pie)\b'
regex2 = r'(?i)(?=.*?\bsweet pie\b)(?=.*?\bsavoury pie\b)(?=.*?\bmeringue\b)|(?=.*?\bchicken pie\b)(?=.*?\bbeef pie\b)'
Обратите внимание, что второе регулярное выражение эффективно имеет привязку ^
в начале, так как оно используется с методом re.match
. Это также включает несколько дополнительных (незначительных) настроек; удаление ненужных групп захвата и изменение жадной звездочки на ленивый. Это решение работает почти в 10 раз быстрее, чем оригинал на моей коробке Win32 с Python 3.0.1.
Дополнительно: Так почему же это быстрее? Давайте посмотрим на простой пример, который описывает, как работает «движок» NFA regex. (Обратите внимание, что следующее описание происходит от классической работы по этому вопросу: Освоение регулярных выражений (3-е издание) Джеффри Фридлом (MRE3), которая объясняет все эти вещи эффективности очень подробно - очень рекомендуется.) Допустим, у вас есть целевая строка, содержащая одно слово, и вы хотите, чтобы регулярное выражение проверяло, является ли это слово: "apple"
. Вот два регулярных выражения, которые можно составить для выполнения этой работы:
re1 = r'^apple'
re2 = r'apple'
s = r'orange'
Если ваша целевая строка: apple
(или applesauce
или apple-pie
и т. Д.), То оба регулярных выражения будут успешно совпадать очень быстро. Но если ваша целевая строка говорит: orange
, ситуация другая. Механизм регулярных выражений NFA должен попробовать все возможные перестановки регулярного выражения в целевой строке , прежде чем он сможет объявить ошибку совпадения. Способ, которым работает «движок» регулярных выражений, заключается в том, что он сохраняет внутренний указатель на свое текущее местоположение в целевой строке и второй указатель на местоположение в шаблоне регулярных выражений, а также продвигает эти указатели в процессе своей деятельности. Обратите внимание, что эти указатели указывают на местоположения между символами, и для начала указатель целевой строки устанавливается на местоположение перед первой буквой, если целевая строка.
re1: Первый токен в регулярном выражении - это ^
начало привязки строки. Этот «якорь» является одним из специальных выражений «утверждения», которое соответствует location в целевой строке и фактически не соответствует никаким символам. (Lookahead и lookbehind, а также выражения границы слова \b
также являются утверждениями, которые соответствуют местоположению и не «потребляют» никаких символов.) Хорошо, указатель целевой строки инициализируется в месте перед первой буквой слова orange
, механизм регулярных выражений проверяет, совпадает ли привязка ^
, и это происходит (потому что это местоположение, фактически, является началом строки). Таким образом, указатель шаблона перемещается к следующему токену в регулярном выражении, букве a
. (Указатель целевой строки не является расширенным). Затем он проверяет, соответствует ли литерал регулярного выражения a
целевому строковому символу o
. Это не соответствует. На этом этапе движок регулярных выражений достаточно умен, чтобы знать, что регулярные выражения никогда не преуспеют ни в каких других местах в пределах целевой строки (потому что ^
никогда не может совпадать нигде, кроме как в начале). Таким образом, он может объявить ошибку совпадения.
re2: В этом случае двигатель начинается с проверки, соответствует ли первый символ char a
первому целевому символу 'o'.Еще раз, это не так.Однако в этом случае двигатель регулярных выражений не выполняется!Он определил, что шаблон не будет совпадать в первом местоположении, но он должен попытаться (и потерпеть неудачу) в ALL местоположениях с целевой строкой, прежде чем он сможет объявить ошибку совпадения.Таким образом, движок продвигает целевой указатель строки к следующему местоположению (Фридл называет это «передачей»).Для каждого «удара» он сбрасывает указатель шаблона обратно в начало.Таким образом, он проверяет первый токен в шаблоне a
против второго символа в строке: r
.Это также не совпадает, поэтому передача снова переходит к следующему месту в строке.В этот момент он проверяет первый символ шаблона a
против третьего символа цели: a
, что соответствует .Движок продвигает оба указателя и проверяет второй символ в регулярном выражении p
против четвертого символа в цели n
.Это не удается.В этот момент двигатель объявляет о неисправности в месте перед a
в orange
, а затем снова сталкивается с n
.Это продолжается до тех пор, пока не произойдет сбой в каждом месте целевой строки, после чего он может объявить общий сбой соответствия.
Для длинных строк объекта эта дополнительная ненужная работа может занять много времени.Создание точного и эффективного регулярного выражения равнозначно искусству и науке.И чтобы создать действительно замечательное регулярное выражение, нужно точно понимать, как на самом деле работает двигатель под капотом.Получение этих знаний требует времени и усилий, но затраченное время (по моему опыту) окупится во много раз.И действительно, есть только одно хорошее место для эффективного обучения этим навыкам - сидеть и изучать Освоение регулярных выражений (3-е издание) , а затем практиковать изученные приемы.Я могу честно сказать, что это самая полезная книга, которую я когда-либо читал.(Это даже смешно! )
Надеюсь, это поможет!8 ^)