Кажется, это должно быть довольно тривиально, но я новичок в Python и хочу сделать это самым питоническим способом.
Я хочу найти индекс, соответствующий n-му вхождению подстроки в строке.
Должно быть что-то эквивалентное тому, что Я ХОЧУ делать, а именно
mystring.find("substring", 2nd)
Как этого добиться в Python?
Ответы:
Я думаю, что итеративный подход Марка был бы обычным.
Вот альтернатива с разделением строк, которая часто может быть полезна для поиска связанных процессов:
И вот быстрый (и несколько грязный, поскольку вам нужно выбрать немного мякины, не совпадающей с иглой) однострочник:
источник
.rfind('XXX')
, но это развалится, если'XXX'
все равно появится позже во входных данных.Вот более питоническая версия простого итеративного решения:
Пример:
Если вы хотите найти n-е перекрывающееся вхождение
needle
, вы можете увеличивать его на1
вместоlen(needle)
, например:Пример:
Ее легче читать, чем версию Марка, и она не требует дополнительной памяти для разделяемой версии или импорта модуля регулярных выражений. Он также придерживается нескольких правил дзен питона , в отличие от различных
re
подходов:источник
Это найдет второе вхождение подстроки в строку.
Изменить: я не особо задумывался о производительности, но быстрая рекурсия может помочь найти n-е вхождение:
источник
n
подстрок встречается меньше, чем вхождений. (В этом случае возвращаемое значение будет периодически проходить через все позиции появления).Понимая, что регулярное выражение - не всегда лучшее решение, я бы, вероятно, использовал его здесь:
источник
(m.start() for m in re.finditer(r"ab",s))[2]
itertools.islice
функции:next(islice(re.finditer(r"ab",s), 2, 2+1)).start()
Я предлагаю результаты сравнительного анализа наиболее известных подходов, представленных на данный момент, а именно: @bobince
findnth()
(на основеstr.split()
) и @ tgamblin или @Mark Byersfind_nth()
(на основеstr.find()
). Я также сравню с расширением C (_find_nth.so
), чтобы увидеть, насколько быстро мы можем работать. Вотfind_nth.py
:Конечно, производительность имеет наибольшее значение, если строка большая, поэтому предположим, что мы хотим найти 1000001-ю новую строку ('\ n') в файле размером 1,3 ГБ с именем 'bigfile'. Чтобы сэкономить память, мы хотели бы поработать над
mmap.mmap
объектным представлением файла:Уже есть первая проблема
findnth()
, так какmmap.mmap
объекты не поддерживаютsplit()
. Таким образом, нам действительно нужно скопировать весь файл в память:Ой! К счастью,
s
все еще умещается в 4 ГБ памяти моего Macbook Air, так что давайте посмотримfindnth()
:Явно ужасная производительность. Посмотрим, как работает подход, основанный на
str.find()
:Намного лучше! Очевидно,
findnth()
проблема заключается в том, что он вынужден копировать строку во времяsplit()
, а это уже второй раз, когда мы скопировали 1,3 ГБ данных примерно после этогоs = mm[:]
. А вот во втором преимуществоfind_nth()
: Мы можем использовать егоmm
непосредственно, например , что нулевые копии файла требуется:Кажется, есть небольшое снижение производительности при работе с
mm
vs.s
, но это показывает, что мыfind_nth()
можем получить ответ за 1,2 с по сравнению сfindnth
с общим значением 47 с.Я не нашел случаев, когда
str.find()
основанный подход был значительно хуже, чемstr.split()
основанный подход, поэтому на данном этапе я бы сказал, что следует принять ответ @ tgamblin или @Mark Byers вместо ответа @bobince.В моем тестировании версия
find_nth()
выше была самым быстрым решением на чистом Python, которое я мог придумать (очень похоже на версию @Mark Byers). Посмотрим, насколько лучше мы можем сделать с модулем расширения C. Вот_find_nthmodule.c
:Вот
setup.py
файл:Установить как обычно с помощью
python setup.py install
. Код C здесь имеет преимущество, поскольку он ограничен поиском отдельных символов, но давайте посмотрим, насколько это быстро:Ясно, что еще немного быстрее. Интересно, что на уровне C нет разницы между случаями in-memory и mmapped. Также интересно отметить , что
_find_nth2()
, в основе которой лежитstring.h
«Smemchr()
библиотечной функции, теряет против простой реализации в_find_nth()
: Дополнительному„оптимизации“вmemchr()
по- видимому , отражающиеся ...В заключение, реализация в
findnth()
(на основеstr.split()
) - действительно плохая идея, так как (а) она ужасно работает для больших строк из-за необходимого копирования, и (б) она вообще не работает сmmap.mmap
объектами. Реализация вfind_nth()
(на основеstr.find()
) должна быть предпочтительнее при любых обстоятельствах (и, следовательно, быть принятым ответом на этот вопрос).Есть еще немало возможностей для улучшения, поскольку расширение C работает почти в 4 раза быстрее, чем чистый код Python, что указывает на то, что может быть случай для специальной библиотечной функции Python.
источник
Самый простой способ?
источник
Я бы, наверное, сделал что-то подобное, используя функцию поиска, которая принимает параметр индекса:
Я думаю, это не особенно Pythonic, но все просто. Вместо этого вы можете сделать это с помощью рекурсии:
Это функциональный способ решить эту проблему, но я не знаю, делает ли это его более Pythonic.
источник
for _ in xrange(n):
можно использовать вместоwhile n: ... n-=1
return find_nth(s, x, n - 1, i + 1)
должно бытьreturn find_nth(s, x, n - 1, i + len(x))
. Ничего страшного, но экономит время вычислений.Это даст вам массив начальных индексов для совпадений
yourstring
:Тогда ваша n-я запись будет такой:
Конечно, вы должны быть осторожны с границами индекса. Вы можете получить количество таких экземпляров
yourstring
:источник
Вот еще один подход с использованием re.finditer.
Разница в том, что это смотрит в стог сена только настолько, насколько это необходимо.
источник
Вот еще одна
re
+itertools
версия, которая должна работать при поиске либо a,str
либо aRegexpObject
. Я свободно признаю, что это, вероятно, чрезмерно спланировано, но по какой-то причине меня это развлекало.источник
Основываясь на ответе modle13 , но без
re
зависимости модуля.Мне бы хотелось, чтобы это был встроенный строковый метод.
источник
источник
Предлагаем еще одно «хитрое» решение, в котором используются
split
иjoin
.В вашем примере мы можем использовать
источник
источник
find_nth('aaa', 'a', 0)
возвращается,1
пока он должен вернуться0
. Вам нужно что-то вродеi = s.find(substr, i) + 1
и потом вернутьi - 1
.Решение без использования циклов и рекурсии.
источник
Для особого случая, когда вы ищете n-е вхождение символа (то есть подстроку длины 1), следующая функция работает, создавая список всех позиций появления данного символа:
Если количество
n
вхождений данного символа меньше , он выдастIndexError: list index out of range
.Это получено из ответа @ Zv_oDD и упрощено для случая одного символа.
источник
Замена одного вкладыша - это здорово, но работает только потому, что XX и стержень имеют одинаковую длину.
Хорошее и общее определение будет:
источник
Это тот ответ, который вам действительно нужен:
источник
Вот мое решение для поиска
n
появленияb
в строкеa
:Это чистый Python и итеративный. Если 0 или
n
слишком большое значение, возвращается -1. Он однострочный и может использоваться напрямую. Вот пример:источник
Def:
Использовать:
Вывод:
источник
Как насчет:
источник