Несколько важных факторов, которые следует учитывать при взвешивании различных подходов:
- Многострочный по сравнению с одной строкой
- Длина строк
- Длина шаблона поиска
- Частота поиска, совпадающая с
- Что делать, если до / после осталось менее 5 слов
- Как обрабатывать несловные, непробельные символы (символы новой строки и пунктуации)
- Без учета регистра?
- Что делать с перекрывающимися совпадениями?Например, если текст
We are the knights who say NI! NI NI NI NI NI NI NI NI
и вы ищете NI
, что вы возвращаете?Это случится с вами? - Что, если в ваших данных будет
###
? - Вы бы предпочли пропустить некоторые или выдать дополнительные неверные результаты?Там могут быть некоторые компромиссы, особенно с грязными данными реального мира.
Вы можете попробовать регулярное выражение ...
import re
zen = """Beautiful is better than ugly. \
Explicit is better than implicit. \
Simple is better than complex. \
Complex is better than complicated. \
Flat is better than nested. \
Sparse is better than dense. \
Readability counts. \
Special cases aren't special enough to break the rules. \
Although practicality beats purity. \
Errors should never pass silently. \
Unless explicitly silenced. \
In the face of ambiguity, refuse the temptation to guess. \
There should be one-- and preferably only one --obvious way to do it. \
Although that way may not be obvious at first unless you're Dutch. \
Now is better than never. \
Although never is often better than *right* now. \
If the implementation is hard to explain, it's a bad idea. \
If the implementation is easy to explain, it may be a good idea. \
Namespaces are one honking great idea -- let's do more of those!"""
searchvar = 'Dutch'
dutchre = re.compile(r"""((?:\S+\s*){,5})(%s)((?:\S+\s*){,5})""" % searchvar, re.IGNORECASE | re.MULTILINE)
print dutchre.findall(zen)
#[("obvious at first unless you're ", 'Dutch', '. Now is better than ')]
Альтернативный подход, который приводит к худшим результатам IMO...
def splitAndFind(text, phrase):
text2 = text.replace(phrase, "###").split("###")
if len(text2) > 1:
return ((text2[0].split()[-5:], text2[1].split()[:5]))
print splitAndFind(zen, 'Dutch')
#(['obvious', 'at', 'first', 'unless', "you're"],
# ['.', 'Now', 'is', 'better', 'than'])
В iPython вы можете легко рассчитать время:
timeit dutchre.findall(zen)
1000 loops, best of 3: 814 us per loop
timeit 'Dutch' in zen
1000000 loops, best of 3: 650 ns per loop
timeit zen.find('Dutch')
1000000 loops, best of 3: 812 ns per loop
timeit splitAndFind(zen, 'Dutch')
10000 loops, best of 3: 18.8 us per loop