閉じる

Pythonのfind()とindex()で文字列を検索する方法

文字列から特定の部分文字列を見つける時、Pythonではfind()index()を使います。

どちらも似ていますが、見つからない時の挙動が異なります。

本記事では、基本から検索範囲の指定、複数一致の列挙、例外の扱い、大小文字の違いへの対処まで、初心者でも着実に理解できるよう段階的に解説します。

Pythonのfind()とindex()の基本

基本の使い方(部分文字列の位置を取得)

最初の一致位置(先頭から数えた0始まりのインデックス)を返すのがfind()index()の基本動作です。

見つかった位置の数値で返るため、そのままスライスや置換の基点にできます。

以下は"banana"から"na"を探す例です。

Python
# 基本の使い方: 最初の一致位置を取得
s = "banana"

print("s =", s)
print("find('na'):", s.find("na"))   # 最初の一致はインデックス2
print("index('na'):", s.index("na")) # 使い方はfindと同じ。戻り値も同じ
print("find('xyz'):", s.find("xyz")) # 見つからない場合の挙動は後述

# 右側から探すメソッドもあります(後述)
print("rfind('na'):", s.rfind("na"))
print("rindex('na'):", s.rindex("na"))
実行結果
s = banana
find('na'): 2
index('na'): 2
find('xyz'): -1
rfind('na'): 4
rindex('na'): 4

インデックスは0始まりであることに注意してください。

例えば"banana""b"は0番目、"a"は1番目という数え方です。

戻り値の違い(-1とValueError)

最大の違いは、見つからなかった時にfind()-1を返し、index()ValueErrorを送出する点です。

用途に応じて使い分けると安全です。

以下の比較表を目安にしてください。

メソッド検索方向見つからない時典型的な用途
find(sub[, start[, end]])左から-1を返す条件分岐で存在確認しやすい
index(sub[, start[, end]])左からValueErrorを送出絶対に存在すると仮定したい時
rfind(sub[, start[, end]])右から-1を返す最後の一致位置が欲しい時
rindex(sub[, start[, end]])右からValueErrorを送出最後の一致が必須の時

例外処理が不要ならfind()、必ず存在しないと困る前提ならindex()を選ぶのが基本指針です。

rfind()・rindex()で後ろから検索

最後に出現する一致位置が必要な時はrfind()またはrindex()を使います。

動作は左からの版と同じで、rfind()は-1、rindex()はValueErrorを採ります。

Python
# 後ろから検索する例
s = "abracadabra"
print("最後の'a'の位置(rfind):", s.rfind("a"))   # 10
print("最後の'ra'の位置(rfind):", s.rfind("ra")) # 9
実行結果
最後の'a'の位置(rfind): 10
最後の'ra'の位置(rfind): 9

start/endで検索範囲を指定

startで開始位置をずらす

2番目以降の一致を探したい時はstart引数で検索の開始位置を後ろへずらします。

これにより、先頭で見つかった一致を飛ばして検索できます。

Python
# startで検索開始位置を指定
s = "abracadabra"
print("s =", s)
print("find('a', 3):", s.find("a", 3))  # 3番目以降で最初の'a' -> 3
print("find('a', 4):", s.find("a", 4))  # 4番目以降で最初の'a' -> 5
実行結果
s = abracadabra
find('a', 3): 3
find('a', 4): 5

見つかった位置の直後から再検索することで、複数一致の列挙にもつなげられます(後述)。

endで終端を区切る

end終端インデックスの直前までを検索する、いわゆる排他的上限です。

スライスと同じ感覚で覚えると混乱しません。

Python
# endは排他的(直前まで)
s = "abracadabra"
print("find('a', 0, 1):", s.find("a", 0, 1))  # インデックス0は含まれる -> 0
print("find('a', 1, 2):", s.find("a", 1, 2))  # 'b'のみの範囲 -> -1
print("find('ra', 0, 4):", s.find("ra", 0, 4))  # "abra"内 -> 2
print("find('ra', 0, 3):", s.find("ra", 0, 3))  # "abr"内 -> -1
実行結果
find('a', 0, 1): 0
find('a', 1, 2): -1
find('ra', 0, 4): 2
find('ra', 0, 3): -1

開始位置と同様にrfind()rindex()にもstart/endは効きます

スライス不要の区間検索

部分列にしたい区間が決まっているなら、s[start:end].find(...)のようなスライスは不要で、find(sub, start, end)で直接指定できます。

余計な一時文字列を作らないため、可読性と効率が上がります。

Python
# スライス不要で区間検索
s = "abracadabra"
print("find('cad', 0, 7):", s.find("cad", 0, 7))
print("s[:7].find('cad'):", s[:7].find("cad"))  # 結果は同じ
実行結果
find('cad', 0, 7): 4
s[:7].find('cad'): 4

複数一致の見つけ方

findを反復して次の一致を探す

基本パターンは、見つかった位置の直後からfind()を繰り返す方法です。

これにより、非オーバーラップで順に列挙できます。

Python
# 非オーバーラップで複数一致を列挙
s = "banana"
sub = "na"

positions = []
start = 0
while True:
    i = s.find(sub, start)  # start以降で検索
    if i == -1:
        break
    positions.append(i)
    start = i + len(sub)    # 見つかった部分の直後から再開(非オーバーラップ)

print(positions)  # 期待: [2, 4]
実行結果
[2, 4]

start = i + len(sub)とすることで、同じ一致を二重に数えないのがポイントです。

すべてのインデックスを列挙する

関数化しておくと再利用しやすく、テストやロギングにも便利です。

次は、1文字ずつずらして走査し、すべての一致位置を返す実装です。

Python
# オーバーラップも含めてすべての一致位置を列挙
def find_all(text: str, sub: str) -> list[int]:
    """text中に現れるsubの開始インデックスを(重なりも含めて)昇順で返す"""
    result: list[int] = []
    i = -1
    while True:
        i = text.find(sub, i + 1)  # 前回位置の次の位置から再検索
        if i == -1:
            break
        result.append(i)
    return result

print(find_all("banana", "na"))  # [2, 4]
print(find_all("aaaa", "aa"))    # [0, 1, 2] (オーバーラップ例)
実行結果
[2, 4]
[0, 1, 2]

1文字ずつ開始位置を進めるため、重なり合う一致も見逃しません。

オーバーラップの一致を扱う

非オーバーラップで数えたい場合は+ len(sub)で飛ばし、重なりも数えたい場合は+ 1で1文字ずつ進めます。

違いは出力から一目瞭然です。

Python
# 非オーバーラップ版とオーバーラップ版の比較
def find_all_non_overlap(text: str, sub: str) -> list[int]:
    result = []
    start = 0
    while True:
        i = text.find(sub, start)
        if i == -1:
            break
        result.append(i)
        start = i + len(sub)  # 非オーバーラップなので長さ分進める
    return result

def find_all_overlap(text: str, sub: str) -> list[int]:
    result = []
    start = 0
    while True:
        i = text.find(sub, start)
        if i == -1:
            break
        result.append(i)
        start = i + 1          # オーバーラップを許すので1文字だけ進める
    return result

print("非オーバーラップ:", find_all_non_overlap("aaaa", "aa"))
print("オーバーラップ:", find_all_overlap("aaaa", "aa"))
実行結果
非オーバーラップ: [0, 2]
オーバーラップ: [0, 1, 2]

用途に応じて「数え方」を明確に決めてから関数を選ぶと混乱しません。

エラーと例外の対処

見つからない時の処理(findは-1)

find()は見つからないと-1なので、条件分岐で安全に扱えます。

次のように-1判定で存在確認ができます。

Python
# findは-1で「未検出」を表す
s = "python"
pos = s.find("Java")
if pos == -1:
    print("見つかりませんでした")
else:
    print(f"見つかった位置: {pos}")
実行結果
見つかりませんでした

数値の-1はスライスの負のインデックスと紛らわしいため、必ず明示的に比較してから使うと安全です。

indexのValueErrorをtry/exceptで扱う

index()は見つからないとValueErrorを送出するため、例外処理で包みます。

存在が前提の場面で「前提が崩れた」ことを即座に検知できる利点があります。

Python
# indexは見つからないとValueError
s = "python"
try:
    pos = s.index("Java")
    print(f"見つかった位置: {pos}")
except ValueError as e:
    print("ValueErrorを捕捉:", e)
実行結果
ValueErrorを捕捉: substring not found

入力検証やアサーションの代替としてindex()を使うと、異常系が明確になります。

大文字小文字の違いに対処(lowerで正規化)

大小文字を無視して検索したい時は、両者をlower()casefold()で正規化してからfind()します。

特に国際化対応ではcasefold()がより強力です。

Python
# lower()を使ったケース非依存検索
text = "Hello World"
needle = "world"

pos = text.lower().find(needle.lower())
print("位置:", pos)  # 6
if pos != -1:
    # 元の文字列から該当部分を取り出せる
    print("一致部分:", text[pos:pos+len(needle)])

# casefold()はより広い言語で堅牢
print("lowerでの一致:", "straße".lower().find("STRASSE".lower()))     # -1 (合致しないことがある)
print("casefoldでの一致:", "straße".casefold().find("STRASSE".casefold())) # 0 (一致)
実行結果
位置: 6
一致部分: World
lowerでの一致: -1
casefoldでの一致: 0

UI表示やログでは元の表記を保ちたいため、検索だけ正規化し、表示は原文を使うのが実務的です。

まとめ

文字列検索の基本はfind()index()で、違いは「見つからない時」の挙動にあります。

開始位置startと終端endを使えばスライスなしで区間検索ができ、rfind()/rindex()で最後の一致を簡単に得られます。

複数一致はfind()の反復で列挙でき、非オーバーラップとオーバーラップのどちらを数えるかを方針として決めて実装します。

エラー処理ではfind()-1判定とindex()ValueError捕捉を適切に使い分け、大小文字の違いはlower()casefold()で正規化して対応します。

これらを押さえておけば、実務の文字列処理で迷う場面は大きく減らせます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!