部分文字列のすべての出現を見つける方法は?


365

Pythonはありstring.find()string.rfind()文字列のサブストリングのインデックスを取得します。

string.find_all()見つかったすべてのインデックスを返すことができるようなものがあるかどうか疑問に思っています(最初から最初のものだけでなく、最後から最初のものも)。

例えば:

string = "test test test test"

print string.find('test') # 0
print string.rfind('test') # 15

#this is the goal
print string.find_all('test') # [0,5,10,15]

11
何を'ttt'.find_all('tt')返すべきですか?
サンティアゴアレッ

2
「0」を返す必要があります。もちろん、完全な世界ではも存在する必要があり'ttt'.rfind_all('tt')、「1」を返す必要があります
nukl

回答:


523

探していることを行う単純な組み込みの文字列関数はありませんが、より強力な正規表現を使用できます。

import re
[m.start() for m in re.finditer('test', 'test test test test')]
#[0, 5, 10, 15]

重複する一致を見つけたい場合、先読みはそれを行います:

[m.start() for m in re.finditer('(?=tt)', 'ttt')]
#[0, 1]

オーバーラップのない逆方向検索が必要な場合は、ポジティブとネガティブの先読みを次のような式に組み合わせることができます。

search = 'tt'
[m.start() for m in re.finditer('(?=%s)(?!.{1,%d}%s)' % (search, len(search)-1, search), 'ttt')]
#[1]

re.finditerジェネレータを返すので[]、上記のを変更して()、リストの代わりにジェネレータを取得することができます。これは、結果を1回だけ繰り返す場合により効率的です。


こんにちは、これに関して[m.start() for m in re.finditer('test', 'test test test test')]、私たちはどのように探すことができますtesttext?それははるかに複雑になりますか?
xpanta 2013年

7
:あなたは、一般的に、正規表現に見てみたいdocs.python.org/2/howto/regex.html。あなたの質問の解決策は次のようになります:[m.start()for m in re.finditer( 'te [sx] t'、 'text test text test')]
Yotam Vaknin

1
この方法を使用する時間の複雑さはどうなりますか?
Pranjal Mittal 2017

1
@PranjalMittal。上限か下限か?最良、最悪、または平均的なケース?
マッドフィジシスト

@marcog部分文字列に括弧またはその他の特殊文字が含まれている場合はどうなりますか?
バナナッチ

109
>>> help(str.find)
Help on method_descriptor:

find(...)
    S.find(sub [,start [,end]]) -> int

したがって、私たちはそれを自分で構築することができます:

def find_all(a_str, sub):
    start = 0
    while True:
        start = a_str.find(sub, start)
        if start == -1: return
        yield start
        start += len(sub) # use start += 1 to find overlapping matches

list(find_all('spam spam spam spam', 'spam')) # [0, 5, 10, 15]

一時的な文字列や正規表現は必要ありません。


22
重複する一致を取得するには、で置き換えるだけstart += len(sub)で十分ですstart += 1
Karl Knechtel、2011年

4
私はあなたの以前のコメントがあなたの答えの追記であるべきだと思います。
tzot

1
あなたのコードはsubstr: "ATAT"を "GATATATGCATATACTT"で検索するために機能しません
Ashish Negi

2
私が付け加えたコメントも見てください。これは、重複する一致の例です。
Karl Knechtel 2013年

4
の動作に合わせるには、の代わりにをre.findall追加することlen(sub) or 1をお勧めします。追加しlen(sub)ない場合、このジェネレータは空の部分文字列で終了しません。
WGH 2015年

45

これは、すべての(つまり重複する)一致を取得する(非常に非効率的な)方法です。

>>> string = "test test test test"
>>> [i for i in range(len(string)) if string.startswith('test', i)]
[0, 5, 10, 15]

25

繰り返しになりますが、古いスレッドですが、これがジェネレーターとプレーンを使用した私のソリューションstr.findです。

def findall(p, s):
    '''Yields all the positions of
    the pattern p in the string s.'''
    i = s.find(p)
    while i != -1:
        yield i
        i = s.find(p, i+1)

x = 'banananassantana'
[(i, x[i:i+2]) for i in findall('na', x)]

戻り値

[(2, 'na'), (4, 'na'), (6, 'na'), (14, 'na')]

3
これは美しく見えます!
fabio.sang

21

re.finditer()重複しない一致に使用できます。

>>> import re
>>> aString = 'this is a string where the substring "is" is repeated several times'
>>> print [(a.start(), a.end()) for a in list(re.finditer('is', aString))]
[(2, 4), (5, 7), (38, 40), (42, 44)]

しかし動作しません

In [1]: aString="ababa"

In [2]: print [(a.start(), a.end()) for a in list(re.finditer('aba', aString))]
Output: [(0, 3)]

12
なぜイテレータからリストを作成するのですか?それは単にプロセスを遅くします。
pradyunsg 2013年

2
aString VS astring;)
NexD。

18

是非、一緒に再帰しましょう。

def locations_of_substring(string, substring):
    """Return a list of locations of a substring."""

    substring_length = len(substring)    
    def recurse(locations_found, start):
        location = string.find(substring, start)
        if location != -1:
            return recurse(locations_found + [location], location+substring_length)
        else:
            return locations_found

    return recurse([], 0)

print(locations_of_substring('this is a test for finding this and this', 'this'))
# prints [0, 27, 36]

この方法では正規表現は必要ありません。


「Pythonで文字列内の部分文字列を検索するための素晴らしい方法はありますか」と思い始めたところ、5分間のグーグル検索の後、コードが見つかりました。共有してくれてありがとう!!!
Geparada 2014

3
このコードにはいくつかの問題があります。それは遅かれ早かれオープンエンドのデータに取り組んでいるので、RecursionError十分に多くのオカレンスがある場合にぶつかります。もう1つは、1つの要素を追加するために各反復で作成される2つの使い捨てリストです。これは、文字列検索関数にとって非常に最適ではなく、何度も呼び出される可能性があります。再帰関数はエレガントで明確に見える場合がありますが、注意して使用する必要があります。
Ivan Nikolaev

11

あなたが単一のキャラクターを探しているだけなら、これはうまくいくでしょう:

string = "dooobiedoobiedoobie"
match = 'o'
reduce(lambda count, char: count + 1 if char == match else count, string, 0)
# produces 7

また、

string = "test test test test"
match = "test"
len(string.split(match)) - 1
# produces 4

私の直感は、これら(特に#2)のどちらもひどく機能的ではないということです。


gr8ソリューション.. split()の使用に感銘を受けました
shantanu pathak

9

これは古いスレッドですが、興味を持って私の解決策を共有したいと思いました。

def find_all(a_string, sub):
    result = []
    k = 0
    while k < len(a_string):
        k = a_string.find(sub, k)
        if k == -1:
            return result
        else:
            result.append(k)
            k += 1 #change to k += len(sub) to not search overlapping results
    return result

サブストリングが見つかった位置のリストを返します。エラーや改善の余地がある場合はコメントしてください。


6

これはre.finditerを使用する私にとってはトリックです

import re

text = 'This is sample text to test if this pythonic '\
       'program can serve as an indexing platform for '\
       'finding words in a paragraph. It can give '\
       'values as to where the word is located with the '\
       'different examples as stated'

#  find all occurances of the word 'as' in the above text

find_the_word = re.finditer('as', text)

for match in find_the_word:
    print('start {}, end {}, search string \'{}\''.
          format(match.start(), match.end(), match.group()))

5

このスレッドは少し古いですが、これは私にとってはうまくいきました:

numberString = "onetwothreefourfivesixseveneightninefiveten"
testString = "five"

marker = 0
while marker < len(numberString):
    try:
        print(numberString.index("five",marker))
        marker = numberString.index("five", marker) + 1
    except ValueError:
        print("String not found")
        marker = len(numberString)

5

あなたが試すことができます :

>>> string = "test test test test"
>>> for index,value in enumerate(string):
    if string[index:index+(len("test"))] == "test":
        print index

0
5
10
15

2

他の人が提供するソリューションは、利用可能なメソッドfind()または利用可能なメソッドに完全に基づいています。

文字列内のすべての部分文字列を見つけるためのコアとなる基本的なアルゴリズムは何ですか?

def find_all(string,substring):
    """
    Function: Returning all the index of substring in a string
    Arguments: String and the search string
    Return:Returning a list
    """
    length = len(substring)
    c=0
    indexes = []
    while c < len(string):
        if string[c:c+length] == substring:
            indexes.append(c)
        c=c+1
    return indexes

strクラスを新しいクラスに継承し、この関数を以下で使用することもできます。

class newstr(str):
def find_all(string,substring):
    """
    Function: Returning all the index of substring in a string
    Arguments: String and the search string
    Return:Returning a list
    """
    length = len(substring)
    c=0
    indexes = []
    while c < len(string):
        if string[c:c+length] == substring:
            indexes.append(c)
        c=c+1
    return indexes

メソッドを呼び出す

newstr.find_all( 'この回答は役に立ちましたか?それからこれに賛成投票してください!'、 'this')


2

この関数は、文字列内のすべての位置を調べるわけではなく、計算リソースを浪費しません。私の試み:

def findAll(string,word):
    all_positions=[]
    next_pos=-1
    while True:
        next_pos=string.find(word,next_pos+1)
        if(next_pos<0):
            break
        all_positions.append(next_pos)
    return all_positions

使用するには、次のように呼び出します。

result=findAll('this word is a big word man how many words are there?','word')

1

ドキュメントで大量のキーワードを探す場合は、フラッシュテキストを使用します

from flashtext import KeywordProcessor
words = ['test', 'exam', 'quiz']
txt = 'this is a test'
kwp = KeywordProcessor()
kwp.add_keywords_from_list(words)
result = kwp.extract_keywords(txt, span_info=True)

Flashtextは、検索語の大きなリストで正規表現よりも高速に実行されます。


0
src = input() # we will find substring in this string
sub = input() # substring

res = []
pos = src.find(sub)
while pos != -1:
    res.append(pos)
    pos = src.find(sub, pos + 1)

1
このコードはOPの問題を解決する可能性がありますが、コードがOPの問題にどのように対処するかについての説明を含めることをお勧めします。このようにして、将来の訪問者はあなたの投稿から学び、それを自分のコードに適用できます。SOはコーディングサービスではなく、知識のリソースです。また、質の高い、完全な回答が支持される可能性が高くなります。これらの機能は、すべての投稿が自己完結型であるという要件とともに、フォーラムとは異なるプラットフォームとしてのSOの強みの一部です。編集して追加情報を追加したり、ソースドキュメントで説明を補足したりできます
SherylHohman

0

これは、ハッカーランクからの同様の質問の解決策です。これがお役に立てば幸いです。

import re
a = input()
b = input()
if b not in a:
    print((-1,-1))
else:
    #create two list as
    start_indc = [m.start() for m in re.finditer('(?=' + b + ')', a)]
    for i in range(len(start_indc)):
        print((start_indc[i], start_indc[i]+len(b)-1))

出力:

aaadaa
aa
(0, 1)
(1, 2)
(4, 5)

-1

スライスすることで、可能なすべての組み合わせを見つけてリストに追加し、count関数を使用してそれが発生する回数を見つけます

s=input()
n=len(s)
l=[]
f=input()
print(s[0])
for i in range(0,n):
    for j in range(1,n+1):
        l.append(s[i:j])
if f in l:
    print(l.count(f))

ときs="test test test test"f="test"あなたのコードのプリント4OPは期待、しかし[0,5,10,15]
barbsan

1つの単語を書い
たら

-2

以下のコードを見てください

#!/usr/bin/env python
# coding:utf-8
'''黄哥Python'''


def get_substring_indices(text, s):
    result = [i for i in range(len(text)) if text.startswith(s, i)]
    return result


if __name__ == '__main__':
    text = "How much wood would a wood chuck chuck if a wood chuck could chuck wood?"
    s = 'wood'
    print get_substring_indices(text, s)

-2

Pythonの方法は次のようになります。

mystring = 'Hello World, this should work!'
find_all = lambda c,s: [x for x in range(c.find(s), len(c)) if c[x] == s]

# s represents the search string
# c represents the character string

find_all(mystring,'o')    # will return all positions of 'o'

[4, 7, 20, 26] 
>>> 

3
1)これは7年前に回答された質問にどのように役立ちますか?2)この方法の使用lambdaはPythonicではなく、PEP8に反します。3)これはOPの状況に適切な出力を提供しません
Wondercricket

Pythonicは「考えられる限り多くのPythonの機能を使用する」という意味ではありません
klutt

-2

簡単に使用できます。

string.count('test')!

https://www.programiz.com/python-programming/methods/string/count

乾杯!


これが答えになるはずです
Maxwell Chandler

8
文字列count()メソッドは、指定された文字列内の部分文字列の出現回数を返します。彼らの場所ではありません。
Astrid

5
これはすべてのケースを満足するわけではありません、s = 'banana'、sub = 'ana'。Subはこの状況で2回発生しますが、
s.sub
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.