Поиск нескольких вхождений строки внутри строки в Python
как найти несколько вхождений строки внутри строки в Python? Рассмотрим это:
>>> text = "Allowed Hello Hollow"
>>> text.find("ll")
1
>>>
Итак, первое вхождение ll
в 1 как и ожидалось. Как мне найти следующее его появление?
тот же вопрос действителен для списка. Рассмотрим:
>>> x = ['ll', 'ok', 'll']
как мне найти все ll
С их индексов?
18 ответов:
используя регулярные выражения, вы можете использовать
re.finditer
чтобы найти все (неперекрывающиеся) случаи:>>> import re >>> text = 'Allowed Hello Hollow' >>> for m in re.finditer('ll', text): print('ll found', m.start(), m.end()) ll found 1 3 ll found 10 12 ll found 16 18
кроме того, если вы не хотите накладные расходы регулярных выражений, вы также можете повторно использовать
str.find
для получения далее:>>> text = 'Allowed Hello Hollow' >>> index = 0 >>> while index < len(text): index = text.find('ll', index) if index == -1: break print('ll found at', index) index += 2 # +2 because len('ll') == 2 ll found at 1 ll found at 10 ll found at 16
Это также работает для списков и других последовательностей.
Я думаю, что вы ищете
string.count
"Allowed Hello Hollow".count('ll') >>> 3
надеюсь, что это помогает
Примечание: это только захватывает неперекрывающиеся случаи
для примера списка используйте понимание:
>>> l = ['ll', 'xx', 'll'] >>> print [n for (n, e) in enumerate(l) if e == 'll'] [0, 2]
аналогично для строк:
>>> text = "Allowed Hello Hollow" >>> print [n for n in xrange(len(text)) if text.find('ll', n) == n] [1, 10, 16]
это будет список соседних запусков "ll', которые могут быть или не быть тем, что вы хотите:
>>> text = 'Alllowed Hello Holllow' >>> print [n for n in xrange(len(text)) if text.find('ll', n) == n] [1, 2, 11, 17, 18]
FWIW, вот несколько не-RE альтернатив, которые я думаю, аккуратнее, чем тыкать тут!--10-->.
первый использует
str.index
и проверяетValueError
:def findall(sub, string): """ >>> text = "Allowed Hello Hollow" >>> tuple(findall('ll', text)) (1, 10, 16) """ index = 0 - len(sub) try: while True: index = string.index(sub, index + len(sub)) yield index except ValueError: pass
второй тест использует
str.find
и проверяет на стража-1
С помощьюiter
:def findall_iter(sub, string): """ >>> text = "Allowed Hello Hollow" >>> tuple(findall_iter('ll', text)) (1, 10, 16) """ def next_index(length): index = 0 - length while True: index = string.find(sub, index + length) yield index return iter(next_index(len(sub)).next, -1)
чтобы применить любую из этих функций к списку, кортежу или другому iterable строк, вы можете использовать более высокого уровня функции -один, что принимает функцию в качестве одного из своих аргументов - вот так:
def findall_each(findall, sub, strings): """ >>> texts = ("fail", "dolly the llama", "Hello", "Hollow", "not ok") >>> list(findall_each(findall, 'll', texts)) [(), (2, 10), (2,), (2,), ()] >>> texts = ("parallellized", "illegally", "dillydallying", "hillbillies") >>> list(findall_each(findall_iter, 'll', texts)) [(4, 7), (1, 6), (2, 7), (2, 6)] """ return (tuple(findall(sub, string)) for string in strings)
пример списка:
In [1]: x = ['ll','ok','ll'] In [2]: for idx, value in enumerate(x): ...: if value == 'll': ...: print idx, value 0 ll 2 ll
Если вы хотите, чтобы все элементы в списке, содержащем 'll', вы также можете сделать это.
In [3]: x = ['Allowed','Hello','World','Hollow'] In [4]: for idx, value in enumerate(x): ...: if 'll' in value: ...: print idx, value ...: ...: 0 Allowed 1 Hello 3 Hollow
>>> for n,c in enumerate(text): ... try: ... if c+text[n+1] == "ll": print n ... except: pass ... 1 10 16
совершенно новый для программирования в целом и работы через онлайн-учебник. Меня также попросили сделать это, но ТОЛЬКО используя методы, которые я изучил до сих пор (в основном строки и петли). Не уверен, что это добавляет какую-либо ценность здесь, и я знаю, что это не так, как вы это сделаете, но я получил его для работы с этим:
needle = input() haystack = input() counter = 0 n=-1 for i in range (n+1,len(haystack)+1): for j in range(n+1,len(haystack)+1): n=-1 if needle != haystack[i:j]: n = n+1 continue if needle == haystack[i:j]: counter = counter + 1 print (counter)
эта версия должна быть линейной по длине строки и должна быть прекрасной, если последовательности не слишком повторяющиеся (в этом случае вы можете заменить рекурсию циклом while).
def find_all(st, substr, start_pos=0, accum=[]): ix = st.find(substr, start_pos) if ix == -1: return accum return find_all(st, substr, start_pos=ix + 1, accum=accum + [ix])
понимание списка bstpierre является хорошим решением для коротких последовательностей, но, похоже, имеет квадратичную сложность и никогда не заканчивается на длинном тексте, который я использовал.
findall_lc = lambda txt, substr: [n for n in xrange(len(txt)) if txt.find(substr, n) == n]
для случайной строки нетривиальной длины две функции дают то же самое результат:
import random, string; random.seed(0) s = ''.join([random.choice(string.ascii_lowercase) for _ in range(100000)]) >>> find_all(s, 'th') == findall_lc(s, 'th') True >>> findall_lc(s, 'th')[:4] [564, 818, 1872, 2470]
но квадратичная версия примерно в 300 раз медленнее
%timeit find_all(s, 'th') 1000 loops, best of 3: 282 µs per loop %timeit findall_lc(s, 'th') 10 loops, best of 3: 92.3 ms per loop
#!/usr/local/bin python3 #-*- coding: utf-8 -*- main_string = input() sub_string = input() count = counter = 0 for i in range(len(main_string)): if main_string[i] == sub_string[0]: k = i + 1 for j in range(1, len(sub_string)): if k != len(main_string) and main_string[k] == sub_string[j]: count += 1 k += 1 if count == (len(sub_string) - 1): counter += 1 count = 0 print(counter)
эта программа подсчитывает число подстрок, даже если они перекрываются без использования регулярных выражений. Но это наивная реализация, и для получения лучших результатов в худшем случае рекомендуется пройти через суффиксное дерево, KMP и другие строковые структуры данных и алгоритмы.
вот моя функция для поиска нескольких вхождений. В отличие от других решений здесь, он поддерживает дополнительные начальные и конечные параметры для нарезки, так же, как
str.index
:def all_substring_indexes(string, substring, start=0, end=None): result = [] new_start = start while True: try: index = string.index(substring, new_start, end) except ValueError: return result else: result.append(index) new_start = index + len(substring)
простой итеративный код, который возвращает список индексов, где подстроки.
def allindices(string, sub): l=[] i = string.find(sub) while i >= 0: l.append(i) i = string.find(sub, i + 1) return l
вы можете разделить, чтобы получить относительные позиции, а затем суммировать последовательные числа в списке и добавить (длина строки * порядок появления) одновременно, чтобы получить нужные строковые индексы.
>>> key = 'll' >>> text = "Allowed Hello Hollow" >>> x = [len(i) for i in text.split(key)[:-1]] >>> [sum(x[:i+1]) + i*len(key) for i in range(len(x))] [1, 10, 16] >>>
Это можно сделать в одну строку, используя список понимания:
example = "a test am I" indicies = [index for index, value in enumerate(example) if value == "a"] print(indices) >>> [0, 7]
аналогичная техника работает для списков:
example = ["a", "b", "c", "a", "d"] indices = [index for index, value in enumerate(example) if value =="a"] print(indices) >>> [0, 3]
может быть, не очень подходящие для Python, но несколько более очевидны. Он возвращает позицию слова, которое выглядело в исходной строке.
def retrieve_occurences(sequence, word, result, base_counter): indx = sequence.find(word) if indx == -1: return result result.append(indx + base_counter) base_counter += indx + len(word) return retrieve_occurences(sequence[indx + len(word):], word, result, base_counter)
этой ссылке объясняет, как сделать все это в O(n) и включает в себя решение в python, а также.
Если вы идете дальше вниз по наборам в 'суффиксных деревьев ' вы могли бы сделать то же самое, если бы у вас была одна большая строка, но вы хотели найти в ней 1000 шаблонов.
Я думаю, что нет необходимости проверять длину текста; просто продолжайте искать, пока не останется ничего, чтобы найти. Вот так:
>>> text = 'Allowed Hello Hollow' >>> place = 0 >>> while text.find('ll', place) != -1: print('ll found at', text.find('ll', place)) place = text.find('ll', place) + 2 ll found at 1 ll found at 10 ll found at 16
вы также можете сделать это с условным пониманием списка следующим образом:
string1= "Allowed Hello Hollow" string2= "ll" print [num for num in xrange(len(string1)-len(string2)+1) if string1[num:num+len(string2)]==string2] # [1, 10, 16]
я случайно получил эту идею совсем недавно. Использование цикла While с соединением строк и поиском строк может работать даже для перекрывающихся строк.
findin = "algorithm alma mater alison alternation alpines" search = "al" inx = 0 num_str = 0 while True: inx = findin.find(search) if inx == -1: #breaks before adding 1 to number of string break inx = inx + 1 findin = findin[inx:] #to splice the 'unsearched' part of the string num_str = num_str + 1 #counts no. of string if num_str != 0: print("There are ",num_str," ",search," in your string.") else: print("There are no ",search," in your string.")
Я любитель программирования на Python (программирование на любом языке, на самом деле), и не уверен, какие еще проблемы он может иметь, но я думаю, что он работает нормально?
Я думаю, что lower () может быть использован где-то в нем тоже, если это необходимо.