Pythonで文字列を検索(〜を含むか判定、位置取得)
Pythonで文字列を検索して特定の文字列を含むか判定したりその位置を取得したりするにはin演算子やfind()メソッドを使う。標準ライブラリのreモジュールを使うと正規表現でより柔軟な処理が可能。
文字列中に含まれる特定の文字・文字列の出現回数(個数)をカウントする方法は以下の記事を参照。
- 関連記事: Pythonで文字・文字列の出現回数をカウント
文字列の抽出・置換・比較については以下の記事を参照。
- 関連記事: Pythonで文字列を抽出(位置・文字数、正規表現)
- 関連記事: Pythonで文字列を置換(replace, translate, re.sub, re.subn)
- 関連記事: Pythonで文字列を比較(完全一致、部分一致、大小関係など)
テキストファイルの中身を検索したい場合は、ファイルを文字列として読み込めばよい。
特定の文字列を含むか判定: in演算子
文字列の中に特定の文字列が含まれているか判定・確認するにはin演算子を使う。含まれているとTrue、含まれていないとFalseを返す。
大文字・小文字は区別される(以降で説明する文字列のメソッドでも同様)。複数の文字列が含まれているかはand(かつ: 両方含む)やor(または: いずれかを含む)を使って判定可能。
s = 'I am Sam'
print('Sam' in s)
# True
print('sam' in s)
# False
print('I' in s and 'Sam' in s)
# True
後述の正規表現を使うとより柔軟な判定ができる。
なお、in演算子はリストやタプル、辞書に対しても使える。if文の条件式でin演算子を使う例など、詳細は以下の記事を参照。
特定の文字列の位置を取得: find(), rfind()
find()
文字列のfind()メソッドで文字列中の特定の文字列の位置を取得できる。
第一引数に指定した文字列が呼び出し元の文字列に含まれている場合はそのスタートの位置(最初の文字の位置)、含まれていない場合は-1が返される。
s = 'I am Sam'
print(s.find('Sam'))
# 5
print(s.find('XXX'))
# -1
位置は0始まり。例の文字列の場合、各文字と位置の対応関係は以下の通り。
I am Sam
01234567
複数の部分文字列が含まれていても、返されるのは最初の部分文字列(一番左側の部分文字列)の位置のみ。すべての位置を取得したい場合は後述の正規表現を使う。
print(s.find('am'))
# 2
第二引数startを指定するとその位置以降が対象、さらに第三引数endを指定するとその位置の前までが対象、すなわち、スライス[start:end]の範囲が対象となる。
print(s.find('am', 3))
# 6
print(s.find('am', 3, 5))
# -1
rfind()
rfind()は右側から(後ろから)検索してその位置を返すメソッド。
部分文字列が複数存在する場合、一番右側の部分文字列の位置が返される。find()と同様、第二引数start、第三引数endも指定可能。
print(s.rfind('am'))
# 6
print(s.rfind('XXX'))
# -1
print(s.rfind('am', 2))
# 6
print(s.rfind('am', 2, 5))
# 2
index(), rindex()
find(), rfind()と似たメソッドにindex(), rindex()がある。
違いは検索した文字列が存在しない場合。find(), rfind()は-1を返すが、index(), rindex()はエラーとなる。
print(s.index('am'))
# 2
# print(s.index('XXX'))
# ValueError: substring not found
print(s.rindex('am'))
# 6
# print(s.rindex('XXX'))
# ValueError: substring not found
大文字・小文字を区別せずに検索
ここまで説明した方法(in演算子や文字列のメソッド)は大文字・小文字を区別して処理する。
大文字・小文字を区別せずに検索したい場合は、元の文字列と検索する文字列をどちらも大文字または小文字に変換して統一すればよい。
文字列をすべて大文字に変換するにはupper()、小文字に変換するにはlower()を使う。
s = 'I am Sam'
print(s.upper())
# I AM SAM
print(s.lower())
# i am sam
print('sam' in s)
# False
print('sam' in s.lower())
# True
print(s.find('sam'))
# -1
print(s.lower().find('sam'))
# 5
なお、日本語のような大文字・小文字の区別がない文字を含む文字列にupper()やlower()メソッドを適用しても問題ない。
print(s.lower())
# 私はsam
print(s.upper())
# 私はSAM
正規表現で判定、位置取得: re.search()
ここからは標準ライブラリのreモジュールで正規表現を使う。reモジュールについての詳細は以下の記事を参照。同じパターンで処理する際に効率的な正規表現パターンのコンパイルについても触れている。
正規表現で特定の文字列を含むか判定するにはre.search()を使う。
第一引数に正規表現パターンの文字列、第二引数に対象の文字列を指定する。正規表現パターンにはメタ文字などを使うこともできるが、ここでは、最もシンプルなパターンとして検索文字列をそのまま使う。
マッチする部分があるとマッチオブジェクト、マッチする部分がないとNoneが返される。
import re
s = 'I am Sam'
print(re.search('Sam', s))
# <re.Match object; span=(5, 8), match='Sam'>
print(re.search('XXX', s))
# None
マッチオブジェクトは常にTrueと判定されるので、if文で条件分岐したい場合は、条件式としてre.search()またはその返り値をそのまま指定すればよい。マッチオブジェクトの詳細については以下の記事を参照。
マッチオブジェクトのgroup()でマッチした文字列(この場合は検索文字列そのまま)、start(), end(), span()で開始位置、終了位置、(開始位置, 終了位置)のタプルがそれぞれ返される。
m = re.search('Sam', s)
print(m.group())
# Sam
print(m.start())
# 5
print(m.end())
# 8
print(m.span())
# (5, 8)
正規表現ですべての結果を取得: re.findall(), re.finditer()
re.search()はマッチする部分が複数含まれていても最初の部分のマッチオブジェクトを返すのみ。
s = 'I am Sam'
print(re.search('am', s))
# <re.Match object; span=(2, 4), match='am'>
re.findall()は、マッチするすべての部分を文字列のリストとして返す。
print(re.findall('am', s))
# ['am', 'am']
マッチするすべての部分の位置を取得したい場合は、re.finditer()とリスト内包表記を組み合わせる。
- 関連記事: Pythonリスト内包表記の使い方
print([m.span() for m in re.finditer('am', s)])
# [(2, 4), (6, 8)]
例はspan()なので(開始位置, 終了位置)のタプルのリストだが、開始位置または終了位置のみのリストを取得したい場合はstart(), end()を使えばよい。
なお、re.finditer()はマッチするすべての部分のマッチオブジェクトのイテレータを返す関数。詳細は以下の記事を参照。
正規表現で複数の文字列を検索
正規表現に詳しくない場合も覚えておくと便利なのが|。正規表現パターンをA|BとするとAまたはBにマッチする。A, Bはただの文字列でも問題ない(もちろんメタ文字などを使ってもよい)。3個以上でも同様にA|B|Cとすればよい。
以下のように、複数の文字列を検索できる。
s = 'I am Sam Adams'
print(re.findall('Sam|Adams', s))
# ['Sam', 'Adams']
print([m.span() for m in re.finditer('Sam|Adams', s)])
# [(5, 8), (9, 14)]
正規表現パターンを活用
正規表現パターンとして検索文字列をそのまま使うと単純な処理しかできないが、正規表現のメタ文字や特殊シーケンスを利用するとより柔軟に検索できる。
s = 'I am Sam Adams'
print(re.findall('am', s))
# ['am', 'am', 'am']
print(re.findall('[a-zA-Z]+am[a-z]*', s))
# ['Sam', 'Adams']
Python(reモジュール)の正規表現も基本的には標準的な正規表現のシンタックスと同じ。ワイルドカード的な指定など、正規表現パターンを活用した基本的な例を以下の記事で紹介しているので参考にされたい。
正規表現で大文字・小文字を区別せずに検索: re.IGNORECASE
re.search()やre.findall()などの関数の引数flagsにre.IGNORECASEを指定することで、大文字・小文字を区別せずに検索できる。
s = 'I am Sam'
print(re.search('sam', s))
# None
print(re.search('sam', s, flags=re.IGNORECASE))
# <re.Match object; span=(5, 8), match='Sam'>