Пошук декількох випадків входження рядка в рядок у Python


81

Як знайти кілька випадків входження рядка всередині рядка в Python? Розглянемо це:

>>> text = "Allowed Hello Hollow"
>>> text.find("ll")
1
>>> 

Отже, перший випадок ll- 1, як очікувалося. Як знайти наступне його виникнення?

Те саме питання стосується і списку. Розглянемо:

>>> x = ['ll', 'ok', 'll']

Як мені знайти все llз їх індексами?


3
>>> text.count ("ll")
blackappy

3
@blackappy це враховує випадки, не локалізує їх
pcko1,

Відповіді:


122

Використовуючи регулярні вирази, ви можете використовувати 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

Це також працює для списків та інших послідовностей.


1
Чи неможливо це зробити без використання регулярних виразів?
user225312

1
Не те, щоб у мене були якісь проблеми, але просто цікаво.
user225312

2
списки не мають find. Але це працює index, вам просто потрібно except ValueErrorзамість тестування на -1
aaronasterling

@ Аарон: Я мав на увазі основну ідею, звичайно, вам доведеться трохи внести її до списків (наприклад, index += 1замість цього).
тикати

4
тепер, коли ви вже згадали все index += 2, якщо застосувати це до рядка 'lllll', він пропустить два з чотирьох випадків 'll'. Найкраще дотримуватися і index += 1для струн.
aaronasterling

31

Я думаю, що ви шукаєте string.count

"Allowed Hello Hollow".count('ll')
>>> 3

Сподіваюся, це допомагає
ПРИМІТКА: це фіксує лише випадки, що не перекриваються


вау дякую. це була дуже проста робоча відповідь
Деррік,

Це відповідь на Підрахувати кількість випадків даного підрядка в рядку , а не на це фактичне запитання, яке вимагає знайти індекси збігів, а не їх кількість ...
Tomerikoo,

26

Для прикладу списку використовуйте розуміння:

>>> 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]

Ого, мені це подобається. Дякую. Це ідеально.
user225312

5
Це вкрай неефективно.
Clément

1
@ Clément розміщує більш ефективний приклад
sirvon

@ Clément print [n for n in xrange (len (text)) if text [n-1: n] == 'll']
Стівен

Я мав на увазі: надрукувати [n для n у xrange (len (текст)), якщо текст [n: n + 2] == 'll']
Стівен

14

FWIW, ось кілька альтернатив, не пов'язаних з RE, які, на мою думку, є акуратнішими, ніж рішення Poke .

Перше використання 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)

Щоб застосувати будь-яку з цих функцій до списку, кортежу чи іншого ітераційного рядка рядків, ви можете використовувати функцію вищого рівня - ту, яка приймає функцію як один із своїх аргументів - як цей:

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)

3

Для прикладу списку:

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

2
>>> for n,c in enumerate(text):
...   try:
...     if c+text[n+1] == "ll": print n
...   except: pass
...
1
10
16

1

Зовсім нове для програмування загалом та роботи з онлайн-підручником. Мене також попросили зробити це, але лише використовуючи методи, які я вивчив до цього часу (в основному рядки та цикли). Не впевнений, що це додає тут якусь цінність, і я знаю, що це не так, як ви це зробили б, але я змусив це працювати з цим:

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)

1

Ця версія повинна бути лінійною за довжиною рядка і повинна бути тонкою, доки послідовності не будуть занадто повторюваними (у цьому випадку ви можете замінити рекурсію на цикл 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

0
#!/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 та інші структури та алгоритми відповідності рядків.


0

Ось моя функція для пошуку кількох випадків. На відміну від інших рішень тут, він підтримує необов'язкові параметри початку та кінця для нарізки, як 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)

0

Простий ітераційний код, який повертає список індексів, де виникає підрядок.

        def allindices(string, sub):
           l=[]
           i = string.find(sub)
           while i >= 0:
              l.append(i)
              i = string.find(sub, i + 1)
           return l

0

Ви можете розділити, щоб отримати відносні позиції, потім підсумувати послідовні числа у списку та додати (довжина рядка * порядок появи) одночасно, щоб отримати потрібні індекси рядків.

>>> 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]
>>> 

0

Можливо, не такий пітонічний, але дещо більш зрозумілий. Він повертає позицію слова, яке переглядається у вихідному рядку.

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)

0

Думаю, немає необхідності перевіряти довжину тексту; просто продовжуйте знаходити, поки не залишиться нічого знайти. Подобається це:

    >>> 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

0

Ви також можете зробити це з умовним розумінням списку таким чином:

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]

0

Я випадково отримав цю ідею зовсім недавно. Використання циклу 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 (насправді програмування будь-якої мови) і не впевнений, які ще проблеми у нього могли бути, але, мабуть, це працює нормально?

Думаю, при необхідності десь у ньому можна було б використовувати і нижній ().


0

Наступна функція знаходить усі входження рядка всередині іншого, інформуючи про позицію, де знайдено кожне входження.

Ви можете викликати функцію, використовуючи тестові приклади в таблиці нижче. Ви можете спробувати зі змішаними словами, пробілами та цифрами.

Функція добре працює з накладеними персонажами.

|         theString          | aString |
| -------------------------- | ------- |
| "661444444423666455678966" |  "55"   |
| "661444444423666455678966" |  "44"   |
| "6123666455678966"         |  "666"  |
| "66123666455678966"        |  "66"   |

Calling examples:
1. print("Number of occurrences: ", find_all("123666455556785555966", "5555"))
   
   output:
           Found in position:  7
           Found in position:  14
           Number of occurrences:  2
   
2. print("Number of occorrences: ", find_all("Allowed Hello Hollow", "ll "))

   output:
          Found in position:  1
          Found in position:  10
          Found in position:  16
          Number of occurrences:  3

3. print("Number of occorrences: ", find_all("Aaa bbbcd$#@@abWebbrbbbbrr 123", "bbb"))

   output:
         Found in position:  4
         Found in position:  21
         Number of occurrences:  2
         

def find_all(theString, aString):
    count = 0
    i = len(aString)
    x = 0

    while x < len(theString) - (i-1): 
        if theString[x:x+i] == aString:        
            print("Found in position: ", x)
            x=x+i
            count=count+1
        else:
            x=x+1
    return count
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.