Pythonではこんなに簡単に文字列を検索できます: in演算子, find, reの正規表現

291, 2021-07-20

目次

Pythonではこんなに簡単に文字列を検索できます

Pythonで文字列を検索するにはin演算子やfind()系のメソッドやcount()、それからreモジュールで正規表現を使います。
この記事では具体的にこれらの演算子やメソッドなどを使った文字列の検索方法について解説します。

結論から言うとPythonによる文字列の検索は↓のようにします。

s = 'Hello, Cat Cat!'

print('Cat' in s)  # in演算子でCatがsに含まれるか判定
# True

print(s.find('Cat'))  # find()でCatの位置を前方から検索
# 7

print(s.rfind('Cat'))  # rfind()でCatの位置を後方から検索
# 11

print(s.count('Cat'))  # count()でCatがsに何個含まれるかカウント
# 2

import re

print(re.search(r'Cat', s))  # 正規表現でCatを検索
# <re.Match object; span=(7, 10), match='Cat'>

print(re.findall(r'Cat', s))  # 正規表現で全てのCatを検索
# ['Cat', 'Cat'] 

print(re.finditer(r'Cat', s))  # 正規表現で全てのCatを検索しイテレーターを取得
# <callable_iterator object at 0x7fb5f1865f40>

↑のように文字列の検索手段はたくさん提供されています。
これは文字列の検索が非常に需要の多い処理だからと思われます。
この記事ではPythonによる文字列の検索について具体的に↓を見ていきます。

  • in演算子で部分文字列か判定

  • str.find()を文字列の位置を検索

  • str.rfind()で文字列の位置を後方から検索

  • count()で文字列をカウント

  • re.search()で正規表現で検索

  • re.findall()で正規表現で複数検索

  • re.finditer()で正規表現で複数検索

  • リストやタプルから文字列を検索

関連記事
Pythonで文字列抽出【インデックスとスライス】
Pythonのjoinで文字列を結合する
Pythonで「Bag Of Words」を使い文章の類似度を調べる【自然言語処理】

in演算子で部分文字列か判定

in演算子を使うと特定の文字列が文字列の中に含まれているか判定することができます。
使い方は↓の通りです。

検索したい文字列 in 検索対象の文字列

↑の式の結果はbool(真偽値)で返ってきます。
たとえば「The Cat」という文字列の中に「Cat」が含まれているか判定する場合は↓のように書きます。

print('Cat' in 'The Cat')  # True

↑はTrueと出力されます。
↓のように「Dog」を検索した場合は「The Cat」には含まれていないので結果はFalseになります。

print('Dog' in 'The Cat')  # False

not in演算子を使うと結果を反転できます。
つまりinの場合は「左の文字列が右の文字列に含まれていたらTrue」でしたが、
not inの場合は「左の文字列が右の文字列に含まれていなかったらTrue」になります。

print('Cat' not in 'The Cat')  # False
print('Dog' not in 'The Cat')  # True

str.find()を文字列の位置を検索

文字列のメソッドstr.find()を使うと特定の文字列を文字列から検索することが出来ます。

find()の構造

find()は↓のような構造になっています。

str.find(sub[, start[, end]])

find()は第1引数substrから検索します。
第2引数startと第3引数endはオプションで、これにはスライスのインデックスを指定します。
find()subが見つかった場合、そのポジションのインデックスを返します。
find()subが見つからなかった場合は-1を返します。

find()による検索

たとえば「The Cat」という文字列から「Cat」という文字列を検索する場合は↓のようにします。

s = 'The Cat'
index = s.find('Cat')
print(index)  # 4

Cat」の「C」文字は「The Cat」の先頭から4番目のインデックス位置にあります。
そのためfind()の結果は4というインデックスになります。

検索対象が見つからなかった場合

第1引数のsubが見つからなかった場合はfind()-1を返します。

s = 'The Cat'
index = s.find('Dog')
print(index)  # -1

スライスによる検索範囲の限定

find()の第2引数startにスライスの開始位置や終了位置を指定すると、対称の文字列の範囲を限定することが出来ます。

s = 'Cat and Cat'
index = s.find('Cat', 3)
print(index)  # 8

↑の場合、find()の第2引数は3になっていますので、対称の文字列の3番目のインデックスから検索が開始されます。
そのため先頭のCatではなく末尾のCatにマッチしたインデックスを取得しています。

引数が文字列でなかった場合の例外

find()の第1引数が文字列でなかった場合は例外TypeErrorが送出されます。

s = 'The Cat'
try:
    s.find(None)
except TypeError as e:
    print(e)  # must be str, not NoneType

str.rfind()で文字列の位置を後方から検索

文字列のメソッドstr.rfind()を使うと文字列の後方から特定の文字列を検索することが出来ます。

rfind()の構造

rfind()の構造はfind()と同じです。
find()との違いは、find()が対称の文字列を前方から検索していくのに対して、rfind()は対称の文字列を後方から検索していく点です。

rfind()による検索

rfind()による検索は↓のようにします。

s = 'One and One'
index = s.rfind('One')
print(index)  # 8

index = s.rfind('Two')
print(index)  # -1

↑の場合「One and One」の中に「One」は2つありますが、rfind()は後方から検索するため、取得できるインデックス位置は2つ目の「One」の位置になっています。

count()で文字列をカウント

文字列のメソッドであるstr.count()を使うと、文字列の中に特定の文字列がいくつ含まれているかカウントすることが出来ます。

count()の構造

count()の構造は↓のようになっています。

str.count(sub[, start[, end]])

引数の役割などはfind()と同じです。
count()は第1引数のsubstrにいくつ含まれているかカウントして返します。
第2引数startと第3引数endにはスライスのインデックスを指定します。

文字列のカウント

たとえば「One and One」という文字列の中に「One」がいくつ含まれているかカウントしたい場合は↓のようにします。

s = 'One and One'
n = s.count('One')
print(n)  # 2

文字列が見つからなかった場合

目的の文字列が見つからなかった場合はカウントは0になります。

s = 'One and One'
n = s.count('Two')
print(n)  # 0

スライスによるカウント範囲の限定

count()の第2引数startにスライスの開始位置を指定すると、対象文字列の範囲を限定出来ます。

s = 'One and One'
n = s.count('One', 3)
print(n)  # 1

↑の場合、start3になっているので「One and One」のうち、2番目の「One」のみがカウントされます。
第4引数のendにスライスの終了位置を指定するとさらに対象文字列の範囲を限定出来ます。

s = 'One and One and One'
n = s.count('One', 3, 11)
print(n)  # 1

↑の場合、start3end11になっているので、真ん中の「One」のみがカウントされています。

引数が文字列でなかった場合の例外

count()の第1引数に文字列でないデータを与えると例外TypeErrorが送出されます。

s = 'One and One'
try:
    s.count(None)
except TypeError as e:
    print(e)  # must be str, not NoneType

re.search()で正規表現で検索

reモジュールのsearch()関数を使うと、正規表現を使って文字列から特定のパターンを検索できます。

search()の構造

search()の構造は↓のようになっています。

re.search(pattern, string, flags=0)

search()の第1引数のpatternには正規表現パターンを指定します。
第2引数のstringには検索対象の文字列を指定します。
第3引数のflagsにはマッチ条件のフラグを設定します。
search()は目的のパターンが見つかった場合はマッチオブジェクトを返します。
返ってくるマッチオブジェクトは最初に見つかった文字列のオブジェクトになります。

パターンによる検索

search()による検索は↓のようにします。

import re


s = 'One Two Three'
m = re.search(r'T[a-z]+', s)

print(m)
# <re.Match object; span=(4, 7), match='Two'>

print(m.group())
# Two

↑の場合、パターンは「r'T[a-z]+'」です。これはTではじまるアルファベットの並びにマッチします。
これにマッチするのは「Two」か「Three」になるので、最初に見つかった「Two」が結果として取れています。

大文字小文字を無視して検索する

search()でアルファベットの大文字小文字を無視して検索するには、search()の第3引数flagsre.IGNORECASEを指定します。

s = 'ONE TWO THREE'
m = re.search(r'two', s, re.IGNORECASE)

print(m)
# <re.Match object; span=(4, 7), match='TWO'>

print(m.group())
# TWO

↑の場合、検索パターンは「r'two'」で小文字のアルファベットですが、re.IGNORECASEを指定して検索しているので「TWO」という文字列にもマッチしています。

re.findall()で正規表現で複数検索

reモジュールのfindall()関数を使うと正規表現パターンで複数の文字列を検索・取得できます。

findall()の構造

findall()は↓のような構造になっています。

re.findall(pattern, string, flags=0)

findall()の第1引数のpatternには正規表現パターンを指定します。
第2引数のstringには検索対象の文字列を指定します。
第3引数のflagsにはマッチ条件のフラグを設定します。
findall()は目的のパターンが見つかった場合は対象の文字列をリストで返します。

文字列を複数検索する

findall()は↓のように使います。

import re


s = 'One Two Three'
elems = re.findall(r'T[a-z]+', s)
print(elems)
# ['Two', 'Three']

findall()の第1引数にはパターンで「r'T[a-z]+」を指定しています。
これはTではじめるアルファベットの並びにマッチします。
One Two Three」の中でこのパターンにマッチするのは「Two」と「Three」です。
結果としてこれらの文字列がリストに保存されて返ってきてます。

大文字小文字を無視して検索する

findall()でアルファベットの大文字小文字を無視して検索するには、findall()の第3引数flagsre.IGNORECASEを指定します。

s = 'one TWO Three'
elems = re.findall(r't[a-z]+', s, re.IGNORECASE)
print(elems)
# ['TWO', 'Three']

↑の場合、パターンは「r't[a-z]+」で小文字のパターンですが、re.IGNORECASEを指定しているので「TWO」や「Three」にもマッチします。

re.finditer()で正規表現で複数検索

reモジュールのfinditer()を使うと、パターンにマッチした複数の文字列をイテレーターで取得することが出来ます。

finditer()の構造

finditer()は↓のような構造になっています。

re.findall(pattern, string, flags=0)

構造的にはre.findall()などと同じです。
返り値がジェネレーターを生成するイテレーターな点が他と違うところです。

文字列を複数検索する

finditer()は↓のように使います。

import re


s = 'One Two Three'
it = re.finditer(r'T[a-z]+', s)
print(it)
# <callable_iterator object at 0x7f33d41fd790>

print(list(it))
# [<re.Match object; span=(4, 7), match='Two'>, <re.Match object; span=(8, 13), match='Three'>]

↑のようにfinditer()で帰ってきたイテレーターをlist()などに渡すと要素がyieldされます。
finditer()はこのように順次取得できる特徴を持っているので、検索結果が巨大になりそうなときに使います。

リストやタプルから文字列を検索

リストやタプルの中に入ってる文字列を検索したい場合があります。

リストの中に文字列が入ってるか判定

リストの中に特定の文字列が含まれているか判定したい時は、文字列の判定の場合と同様にin演算子が使えます。

lis = ['One', 'Two', 'Three']

print('Two' in lis)
# True

print('Four' in lis)
# False

リストの中から特定の文字列のインデックスを探す

リストやタプルのメソッドindex()を使うとリストの中から特定の要素のインデックスを検索できます。

lis = ['One', 'Two', 'Three']
index = lis.index('Two')
print(index)
# 1

↑の場合「Two」はリストの中では1番目に保存されているので、返ってくるインデックスは1になります。
index()は存在しない要素を検索した場合は例外ValueErrorを送出します。

lis = ['One', 'Two', 'Three']
try:
    lis.index('Four')
except ValueError as e:
    print(e)
    # 'Four' is not in list

おわりに

今回はPythonの文字列の検索について見てみました。
文字列の検索はよく使う処理だと思います。

検索を制する者は世界を制す

せやな