python — Поиск подстрок в строке
Есть ли в python встроенные инструкции, которые позволяют осуществлять поиск подстрок в строке?
Строка: Попробуй этих чудесных и спелых фруктов. Попробуешь?
Хочу найти: Поп
Должен получить: [0, 43]
. Т.е. индексы всех вхождений в строку.
- python
- python-3.x
- алгоритм
1
Можно использовать алгоритм Кнута-Морриса-Пратта
def find_all(source, sub): def prefix_func(s): pr = [0] * (len(s)) for i in range(1, len(s)): k = pr[i - 1] while k > 0 and s[k] != s[i]: k = pr[k - 1] if s[k] == s[i]: k = k + 1 pr[i] = k return pr result = prefix_func(sub + "$" + source) # вместо доллара может быть любой другой не встречаюшийся символ return [index for index, element in enumerate(result) if (element >= len(sub))]
Работать будет за линейное время от суммарных длин строк. Если вы хотите искать часто по одной строке, то кажется надо будет написать алгоритм Ахо-Корасика.
5
Можно использовать регулярные выражения:
import re print([m.start() for m in re.finditer('test', 'test test test test')]) #[0, 5, 10, 15]
4
Как на счет такого варианта:
def find_all(s, sub): res = [] cur_pos = 0 for x in s.split(sub)[:-1]: cur_pos += len(x) res.append(cur_pos) cur_pos += len(sub) return res
In [187]: find_all("Попробуй этих чудесных и спелых фруктов. Попробуешь?", "Поп") Out[187]: [0, 41]
5
Вы можете собирать список вхождений строки, каждый раз обрезая уже учтённое вхождение
def findall(s, substr): def searching(s, substr): offset = 0 while s. find(substr) != -1: yield s.find(substr) + offset offset += len(substr) + s.find(substr) s = s[s.find(substr) + len(substr):] return list(searching(s,substr)) print(findall('Попробуй этих чудесных и спелых фруктов. Попробуешь?', 'Поп'))
Важной частью этого метода является хранение «смещения» строки. Чтобы верно указывать индекс вхождения в изначальную строку, нужно помнить, сколько символов мы уже убрали.
Упростить эту конструкцию можно, убирая вхождения справа — тогда индексы не будут смещаться:
def findall(s, substr): def searching(s, substr): while s.rfind(substr) != -1: yield s.rfind(substr) s = s[:s.rfind(substr)] return list(reversed(list(searching(s,substr)))) print(findall('Попробуй этих чудесных и спелых фруктов. Попробуешь?', 'Поп'))
Результат работы обеих вариантов функций:
[0, 41] # да, второе вхождение подстроки находится именно на 41 позиции, а не на 43
1
Зарегистрируйтесь или войдите
Регистрация через Google
Регистрация через Facebook
Регистрация через почту
Отправить без регистрации
Почта
Необходима, но никому не показывается
Отправить без регистрации
ПочтаНеобходима, но никому не показывается
Нажимая на кнопку «Отправить ответ», вы соглашаетесь с нашими пользовательским соглашением, политикой конфиденциальности и политикой о куки
алгоритм — Поиск подстрок в строках списков на Python
Trie или префиксное дерево хорошо подходит для решения задачи. Это дерево для набора строк. В корне хранится dict
со всеми возможными первыми символами строк. В узлах внутри тоже хранятся словари с буквами, которыми можно продолжить строку. Признак конца строки — пустой ключ в словаре. Пример:
import pprint def make_trie(iterable): root = {} for s in iterable: node = root for c in s: node = node.setdefault(c, {}) node[''] = None return root pprint.pprint(make_trie(('then', 'than', 'thing', 'those')))
{'t': {'h': {'a': {'n': {'': None}}, 'e': {'n': {'': None}}, 'i': {'n': {'g': {'': None}}}, 'o': {'s': {'e': {'': None}}}}}}
С помощью префиксного дерева можно проверить, что данный текст содержит какое-нибудь слово из дерева.
Например, чтобы проверить что строка есть в дереве, будем брать из неё символы по одному и спускаться по дереву:
s = ... # строка которую ищем в дереве node = root # корень префиксного дерева for c in s: # символ за символом . .. node = node[c] # ... спускаемся по дереву
Если очередного символа в узле не оказалось, то строки в дереве нет. Если последний узел содержит ключ », то строка в дереве есть.
Так как нам надо проверять не только начало текста, но и любое место внутри, то будем поддерживать список узлов, которые действительны на данном символе. С каждым новым символом текста список пополняется новым элементом — корнем дерева. Все узлы, которые не соответствуют очередному символу из списка удаляются. Чтобы эти операции были быстрыми приходится работать с индексами, что не привычно для Питона:
def contains(text, trie): if '' in trie: return True nodes = [] for c in text: nodes.append(trie) # анализируем строку, которая начинается на этом символе i = 0 while i < len(nodes): node = nodes[i] if c in node: # спуск по дереву node = node[c] nodes[i] = node if '' in node: # признак конца строки в trie return True i += 1 else: # удаляем узел из списка nodes[i] = nodes[-1] nodes.pop() return False
Для проверки эффективности trie были изготовлены тестовые данные. Скрипт generate_sample.py готовит файл со строками для поиска (50’000 строк от 10 до 20 символов) …
... uctufrxhfomiuwrhvkyy hbzkmicgsw gupmuoeiehxrrix nsmlheqpcybdeufzvnt mmtoqiravxd ...
… и текст в котором они ищутся (20’000 строк длиной от 20 до 100 символов) …
... oeosbugxnbfvqgfonutgbzrmmuzumrglpphrqritsiwavmwfvdamrlvulfjswnuzsrhikfybbzxajlfxwhtt qizjtyarlbiwnstvtmrqqomblafkhmvwtiocelcyczobausadcudkzykcgyzwajxzkbdwytlnxdqxxycgsdwsyqtn xtwlvjyxcisvvbvacljxzmdjrhsueyjffdd wyctzgitvbzroiiquohbfostrsvvrorslfevbyhrxqadpytrswk fwxeyfmkqavccxgjrtjsikpazaajpknqiizbpbweublcowani ...
В текст с некоторой вероятностью вставлены слова из словаря, чтобы иногда что-то находилось.
Оригинальный поиск на этих данных выполняется 112 секунд. baseline.py:
with open('patterns.txt') as f: patterns = tuple(line. replace('\n','') for line in f) with open('corpus.txt') as f: for line in f: for p in patterns: if p in line: print(line, end='') break
Поиск с помощью trie
около двух секунд. trie.py:
def make_trie(iterable): ... def contains(text, trie): ... with open('patterns.txt') as f: trie = make_trie(line.replace('\n', '') for line in f) with open('corpus.txt') as f: for line in f: line = line.replace('\n','') if contains(line, trie): print(line)
Так как обработка посимвольная, то Питон не лучший кандидат для быстрого решения. Решение на C или C++ должно работать быстрее раз в 10-20 если не больше.
Python: найти подстроку в строке и вернуть индекс подстроки
спросил
Изменено 2 года, 3 месяца назад
Просмотрено 250 тысяч раз
У меня есть:
функция:
def find_str(s, char)
и строка:
"С Днем Рождения"
,
По сути, я хочу ввести "py"
и вернуть 3
, но вместо этого я продолжаю получать 2
.
Код:
def find_str(s, char): индекс = 0 если символ в s: символ = символ [0] для вп с: если ch в s: индекс += 1 если ч == символ: возвращаемый индекс еще: возврат -1 print(find_str("С днем рождения", "py"))
Не знаю, что случилось!
- python
- строка
- индексация
- подстрока
1
Есть встроенный метод поиска строковых объектов.
с = "С днем рождения" с2 = "ру" печать (s.find (s2))
Python — это «язык с включенными батареями», там написан код, который делает большую часть того, что вы хотите (все, что вы хотите).. если это не домашнее задание 🙂
find
возвращает -1, если строка не может быть найдена.
3
В идеале вы должны использовать str. find или str.index
Ваша проблема в том, что ваш код ищет только первый символ вашей строки поиска, которая (первая) находится в индексе 2.
Вы в основном говорите, если char[0]
находится в s
, приращение индекс
до ch == char[0]
, который вернул 3, когда я тестировал его, но он все еще был неправильным. Вот как это сделать.
по определению find_str(s, char): индекс = 0 если символ в s: с = символ [0] для вп с: если ч == с: если s[index:index+len(char)] == char: возвращаемый индекс индекс += 1 возврат -1 print(find_str("С днем рождения", "py")) print(find_str("С днем рождения", "rth")) print(find_str("С днем рождения", "rh"))
Выдал следующий результат:
3 8 -1
4
В регулярном выражении есть еще одна опция, метод search
import re строка = 'С Днем Рождения' шаблон = 'ру' print(re. search(pattern, string).span()) ## печатает начальный и конечный индексы print(re.search(pattern, string).span()[0]) ## это делает то, что вы хотели
Кстати, если вы хотите найти все вхождения шаблона, а не только первое, вы можете использовать finditer
метод
импорт повторно string = 'я думаю, что то, что там написал тот студент, не совсем так' шаблон = 'это' print([match.start() для совпадения в re.finditer(шаблон, строка)])
, который напечатает все начальные позиции матчей.
Добавление ответа @demented hedgehog при использовании find()
С точки зрения эффективности
Возможно, стоит сначала проверить, находится ли s1 в s2, прежде чем вызывать find()
.
Это может быть более эффективно, если вы знаете, что в большинстве случаев s1 не будет подстрокой s2
Поскольку оператор в
очень эффективен
s1 в s2
Преобразование может быть более эффективным:
index = s2. find(s1)
до
индекс = -1 если s1 в s2: индекс = s2.find(s1)
Это полезно, когда find()
будет часто возвращать -1.
Я обнаружил, что это значительно быстрее с find()
вызывалась много раз в моем алгоритме, поэтому я подумал, что стоит упомянуть
Вот простой подход:
my_string = 'abcdefg' печать (текст. найти ('def'))
Вывод:
3
Если подстроки нет, вы получите -1 . Например:
my_string = 'abcdefg' печать (текст. найти ('xyz'))
Вывод:
-1
Иногда вам может понадобиться создать исключение, если подстроки нет:
my_string = 'abcdefg' print(text.index('xyz')) # Возвращает индекс, только если он присутствует
Вывод:
Трассировка (последний последний вызов):
Файл «test. py», строка 6, в print(text.index(‘xyz’))
ValueError: подстрока не найдена
опоздал на вечеринку, искал то же самое, что и «in» недействительно, я только что создал следующее.
по определению find_str(полный, дополнительный): индекс = 0 суб_индекс = 0 позиция = -1 для ch_i, ch_f в перечислении (полное): если ch_f.lower() != sub[sub_index].lower(): позиция = -1 суб_индекс = 0 если ch_f.lower() == sub[sub_index].lower(): если sub_index == 0 : позиция = ch_i если (len(sub) - 1) <= sub_index : ломать еще: суб_индекс += 1 обратная позиция print(find_str("С днем рождения", "py")) print(find_str("С днем рождения", "rth")) print(find_str("С днем рождения", "rh"))
который производит
3 8 -1
удалить нижний() в случае, если поиск без учета регистра не требуется.
Не отвечая напрямую на вопрос, но недавно я получил аналогичный вопрос, когда меня попросили подсчитать, сколько раз подстрока повторяется в данной строке. Вот функция, которую я написал:
def count_substring(string, sub_string): цент = 0 len_ss = len(sub_string) для i в диапазоне (len (строка) - len_ss + 1): если строка[i:i+len_ss] == sub_string: цент += 1 возврат центов
Функция find(), вероятно, возвращает индекс только первого вхождения. Сохранение индекса вместо простого подсчета может дать нам отдельный набор индексов, в которых подстрока повторяется в строке.
Отказ от ответственности: я «чрезвычайно» новичок в программировании на Python.
Зарегистрируйтесь или войдите в систему
Зарегистрируйтесь с помощью Google
Зарегистрироваться через Facebook
Зарегистрируйтесь, используя электронную почту и пароль
Опубликовать как гость
Электронная почта
Обязательно, но не отображается
Опубликовать как гость
Электронная почта
Требуется, но не отображается
python – Как найти все вхождения подстроки?
Задавать вопрос
спросил
Изменено 28 дней назад
Просмотрено 689 тысяч раз
Python имеет string. find()
и string.rfind()
для получения индекса подстроки в строке.
Мне интересно, есть ли что-то вроде string.find_all()
, которое может вернуть все найденные индексы (не только первый с начала или первый с конца).
Например:
строка = "тест тест тест тест" напечатать string.find('тест') # 0 print string.rfind('тест') # 15 #это цель print string.find_all('test') # [0,5,10,15]
Для подсчет вхождений, см. Подсчет количества вхождений подстроки в строке.
- питон
- строка
5
Не существует простой встроенной строковой функции, которая делает то, что вам нужно, но вы можете использовать более мощные регулярные выражения:
import re [m.start() для m в re.finditer('test', 'test test test test')] #[0, 5, 10, 15]
Если вы хотите найти перекрывающиеся совпадения, это сделает предварительный просмотр:
[m. start() для m в re.finditer('(?=tt)', 'ttt')] #[0, 1]
Если вам нужен обратный поиск без перекрытий, вы можете объединить положительный и отрицательный просмотр в следующее выражение:
search = 'tt' [m.start() для m в re.finditer('(?=%s)(?!.{1,%d}%s)' % (search, len(search)-1, search), 'ttt ')] #[1]
re.finditer
возвращает генератор, поэтому вы можете изменить []
выше на ()
, чтобы получить генератор вместо списка, который будет более эффективным, если вы только перебираете результаты однажды.
9
>>> помощь(ул.найти) Справка по method_descriptor: найти(...) S.find(sub [start [end]]) -> int
Таким образом, мы можем построить его сами:
def find_all(a_str, sub): начало = 0 пока верно: start = a_str.find(sub, start) если start == -1: возврат начало выхода start += len(sub) # используйте start += 1 для поиска перекрывающихся совпадений list(find_all('спам спам спам спам', 'спам')) # [0, 5, 10, 15]
Временные строки или регулярные выражения не требуются.
6
Вот (очень неэффективный) способ получить все (т.е. даже перекрывающиеся) совпадений:
>>> string = "test test test test" >>> [i для i в диапазоне (len(string)) if string.startswith('test', i)] [0, 5, 10, 15]
3
Использовать re.finditer
:
импортировать повторно предложение = ввод ("Дайте мне предложение") word = input("Какое слово вы хотите найти") для совпадения в re.finditer(слово, предложение): печать (match.start(), match.end())
Для слово = "это"
и предложение = "это предложение это это"
это даст результат:
(0, 4) (19, 23) (24, 28)
2
Опять же, старая тема, но вот мое решение с использованием генератора и простого str.find
.
def findall(p, s): '''Выдает все позиции образец p в строке s.''' я = с. найти (р) пока я != -1: выход я я = s.find(p, i+1)
Пример
x = 'banananassantana' [(i, x[i:i+2]) для i в findall('na', x)]
возвращает
[(2, 'нет'), (4, 'нет'), (6, 'нет'), (14, 'нет')]
3
Вы можете использовать re.finditer()
для неперекрывающихся совпадений.
>>> импорт повторно >>> aString = 'это строка, в которой подстрока "is" повторяется несколько раз' >>> print [(a.start(), a.end()) для списка (re.finditer('is', aString))] [(2, 4), (5, 7), (38, 40), (42, 44)]
, но не будет работать для:
В [1]: aString="ababa" В [2]: напечатайте [(a.start(), a.end()) для списка в (re.finditer('aba', aString))] Вывод: [(0, 3)]
2
Давай рекурсируем вместе.
определение location_of_substring (строка, подстрока): """Вернуть список местоположений подстроки.""" substring_length = длина (подстрока) def recurse (locations_found, start): location = string.find (подстрока, начало) если местоположение != -1: вернуть рекурсию (местоположения_найдено + [местоположение], местоположение+подстрока_длина) еще: вернуть location_found вернуть рекурсию ([], 0) print(locations_of_substring('это тест на нахождение этого и этого', 'это')) # печатает [0, 27, 36]
Таким образом, регулярные выражения не нужны.
2
Если вы ищете только один символ, это будет работать:
string = "dooobiedoobiedoobie" совпадение = 'о' уменьшить (количество лямбда, char: количество + 1, если char == соответствует, иначе количество, строка, 0) # производит 7
Кроме того,
строка = "тест тест тест тест" совпадение = "тест" len(string. split(match)) - 1 # производит 4
Я подозреваю, что ни один из них (особенно #2) не обладает ужасной производительностью.
1
это старая тема, но я заинтересовался и хотел поделиться своим решением.
определение find_all (a_string, sub): результат = [] к = 0 пока k < len(a_string): k = a_string.find(sub, k) если к == -1: вернуть результат еще: результат .append(k) k += 1 # измените на k += len(sub), чтобы не искать перекрывающиеся результаты вернуть результат
Должен вернуть список позиций, в которых была найдена подстрока. Пожалуйста, прокомментируйте, если вы видите ошибку или место для улучшения.
Мне помогает re.finditer
import re text = 'Это образец текста для проверки, является ли этот pythonic'\ 'может служить платформой для индексации '\ 'нахождение слов в абзаце. Это может дать '\ 'значения относительно того, где находится слово с '\ «различные примеры, как указано» # найти все вхождения слова as в приведенном выше тексте find_the_word = re. finditer('как', текст) для совпадения в find_the_word: print('начало {}, конец {}, строка поиска \'{}\''. формат(match.start(), match.end(), match.group()))
Этот поток немного устарел, но у меня сработало:
numberString = "onetwothreefourfivesixseveneightnefiveten" тестовая строка = "пять" маркер = 0 в то время как маркер < len(numberString): пытаться: print(numberString.index("пять",маркер)) маркер = числовая строка.индекс ("пять", маркер) + 1 кроме ValueError: print("Строка не найдена") маркер = длина (строка числа)
Вы можете попробовать:
>>> строка = "тест тест тест тест" >>> для индекса, значение в перечислении (строка): если строка[индекс:индекс+(len("тест"))] == "тест": индекс печати 0 5 10 15
При поиске большого количества ключевых слов в документе используйте flashtext
из flashtext import KeywordProcessor слова = ['тест', 'экзамен', 'викторина'] txt = «это тест» kwp = процессор ключевых слов () kwp. add_keywords_from_list(слова) результат = kwp.extract_keywords (txt, span_info = True)
Flashtext работает быстрее, чем регулярное выражение в большом списке поисковых слов.
Эта функция не просматривает все позиции внутри строки, она не тратит вычислительные ресурсы. Моя попытка:
определение findAll(строка,слово): all_positions=[] следующая_позиция=-1 пока верно: next_pos=string.find(слово,next_pos+1) если (следующая_позиция<0): ломать all_positions.append(следующая_позиция) вернуть все_позиции
, чтобы использовать его, назовите его так:
result=findAll('это слово большое, чувак, сколько там слов?','слово')
0
src = input() # мы найдем подстроку в этой строке sub = input() # подстрока разрешение = [] pos = src.find(sub) а поз != -1: res.append(pos) pos = src.find(sub, pos + 1)
1
Вы можете попробовать:
импортировать повторно str1 = "Это платье выглядит хорошо, у тебя хороший вкус в одежде. " substr = "хорошо" результат = [_.start() для _ в re.finditer(substr, str1)] # результат = [17, 32]
1
Какие бы решения не предоставлялись другими, они полностью основаны на доступном методе find() или любых других доступных методах.
Каков базовый алгоритм поиска всех вхождений подстрока в строке?
определение find_all (строка, подстрока): """ Функция: Возврат всего индекса подстроки в строке Аргументы: строка и строка поиска Возврат: Возврат списка """ длина = длина (подстрока) с=0 индексы = [] в то время как c < len (строка): если строка[c:c+length] == подстрока: indexes.append(c) с=с+1 индексы возврата
Вы также можете наследовать класс str новому классу и можете использовать эту функцию ниже.
класс newstr(str): def find_all (строка, подстрока): """ Функция: Возврат всего индекса подстроки в строке Аргументы: строка и строка поиска Возврат: Возврат списка """ длина = длина (подстрока) с=0 индексы = [] в то время как c < len (строка): если строка[c:c+length] == подстрока: indexes. append(c) с=с+1 индексы возврата
Вызов метода
newstr.find_all('Считаете ли вы этот ответ полезным? Тогда проголосуйте за это!','это')
Пифонический способ:
mystring = 'Привет, мир, это должно работать!' find_all = lambda c,s: [x для x в диапазоне (c.find(s), len(c)) if c[x] == s] # s представляет строку поиска # c представляет строку символов find_all(mystring,'o') # вернет все позиции 'o' [4, 7, 20, 26] >>>
2
Это решение похожего вопроса от hackerrank. Я надеюсь, что это может помочь вам.
импорт а = ввод () б = ввод () если б не в а: напечатать((-1,-1)) еще: #создать два списка как start_indc = [m.start() для m в re.finditer('(?=' + b + ')', a)] для i в диапазоне (len (start_indc)): print((start_indc[i], start_indc[i]+len(b)-1))
Вывод:
ааадаа аа (0, 1) (1, 2) (4, 5)
, если вы хотите использовать только numpy, вот решение
импортировать numpy как np S = "тест тест тест тест" S2 = «тест» inds = np. cumsum([len(k)+len(S2) для k в S.split(S2)[:-1]])- len(S2) печать (инд.)
, если вы хотите использовать без re(regex), то:
find_all = lambda _str,_w : [i for i in range(len(_str)) if _str.startswith(_w,i)] строка = "тест тест тест тест тест" print(find_all(string, 'test')) # >>> [0, 5, 10, 15]
Вот решение, которое я придумал, используя выражение присваивания (новая функция начиная с Python 3.8):
string = "test test test test" фраза = "тест" начало = -1 результат = [(начало:= string.find(фраза, начало + 1)) для _ в диапазоне(string.count(фраза))]
Вывод:
[0, 5, 10, 15]
посмотрите на код ниже
#!/usr/bin/env python # кодировка: utf-8 '''黄哥Python''' def get_substring_indices (текст, с): результат = [i для i в диапазоне (длина (текст)) if text.startswith (s, i)] вернуть результат если __name__ == '__main__': text = "Сколько древесины мог бы зажать дровосек, если бы дровосек мог забивать дрова?" с = 'дерево' распечатать get_substring_indices (текст, с)
1
определение find_index (строка, пусть): enumerated = [место для места, буква в перечислении (строка), если буква == пусть] вернуть перечисленные
например:
find_index("привет, найди d", "d")
возвращает:
[4, 7, 13, 15]
1
Не совсем то, что спрашивал OP, но вы также можете использовать функцию разделения, чтобы получить список, где все подстроки не встречаются . OP не указал конечную цель кода, но если ваша цель в любом случае состоит в том, чтобы удалить подстроки, то это может быть простой однострочный код. Вероятно, есть более эффективные способы сделать это с большими строками; регулярные выражения были бы предпочтительнее в этом случае
# Извлечь все неподстроки s = "пример строки" s_no_dash = s.split('-') # >>> s_no_dash # ['an', 'пример', 'строка'] # Или извлеките и соедините их в предложение s_no_dash3 = ' '.join(s.split('-')) # >>> s_no_dash3 # 'пример строки'
Кратко просмотрел другие ответы, так что извините, если это уже там.
по определению count_substring(строка, подстрока): с=0 для я в диапазоне (0, len (строка)-2): если строка[i:i+len(sub_string)] == sub_string: с+=1 вернуться с если __name__ == '__main__': строка = ввод (). полоса () sub_string = ввод (). полоса () count = count_substring(строка, sub_string) распечатать (количество)
2
Я столкнулся с той же проблемой и сделал это:
hw = 'Hello oh World!' list_hw = список (hw) о_in_hw = [] пока верно: о = hw. find('o') если о != -1: o_in_hw.append(o) list_hw[o] = ' ' hw = ''.join(list_hw) еще: печать (o_in_hw) ломать
Я довольно новичок в программировании, поэтому вы, вероятно, можете упростить его (и, если вы планируете использовать его постоянно, конечно, сделайте его функцией).
Все работает так, как и предполагалось для того, что я делал.
Редактировать: Пожалуйста, учтите, что это только для отдельных символов, и это изменит вашу переменную, поэтому вам нужно создать копию строки в новой переменной, чтобы сохранить ее, я не помещал ее в код, потому что это легко и просто только чтобы показать, как я заставил это работать.
Для поиска всех вхождений символа в заданной строке и возврата в виде словаря например: привет результат : {'h':1, 'e':1, 'l':2, 'o':1}
счетчик по умолчанию (строка): результат = {} если (строка): для я в строке: результат[я] = string.