正規表現は難しいと感じている方は多いのではないでしょうか?
正規表現をうまく扱えるようになると、フォームに入力された内容のチェックが非常に簡潔にできるようになります。
ここでは「正規表現って何?」「Pythonで正規表現を使うにはどうすれば良いの?」といった方へ、Pythonで正規表現を使う方法について解説します。
YouTubeも公開していますので、動画や音声で聞きたい方はぜひご覧ください。
正規表現とは
正規表現とは文字列のパターンに対して一致するかどうかをチェックするための処理方法の一つです。プログラミングをやっていたら避けては通れないのにもかかわらず、初めて見た人は暗号のようなとても難解なものに見えてゲンナリするのが正規表現です。
ですが、大体のことを覚えてしまえばそこまで手強い相手でもありません。正規表現では「メタ文字」という記号を組み合わせて文字列のパターンを検出しますが、メタ文字のパターンはそこまで多くありません。
メタ文字一覧
このメタ文字を組み合わせて文字列のパターンマッチをするのが正規表現の使い方です。
メタ文字 | 意味 | 例 | 検索できる文字列の例 |
---|---|---|---|
. | 何かしらの一文字 | 私は.です。 | ◯ 私は人です。 ◯ 私は犬です。 ◯ 私は鳥です。 |
^ | 行の先頭 | ^こんにちは | × 私はこんにちはと挨拶した ◯ こんにちはと挨拶したのは彼女 × あなたはこんにちはと挨拶した |
$ | 行の末尾 | さようなら$ | × 私はさようならを言った × さようならをしたあなた ◯ 別れの言葉はさようなら |
* | 直前の文字の0個以上の繰り返し | はー*い | ◯ はい ◯ はーい ◯ はーーーーーい |
+ | 直前の文字の1個以上の繰り返し | はー+い | × はい ◯ はーい ◯ はーーーーーい |
? | 直前の文字が0個か1個 | はー?い | ◯ はい ◯ はーい × はーーーーーい |
| | いずれかの文字列 | CD|DVD|Blu-ray | ◯ これはCDです。 ◯ これはDVDです。 ◯ これはBlu-rayです。 × これはHDDです。 |
[] | 指定した文字のどれか | [あいうえお] | ◯ あおまきがみあかまきがみきまきがみ ◯ となりのきゃくはよくかきくうきゃくだ × なまむぎなまごめなまたまご |
() | グループ化 | (じゃ)+ーん | ◯ じゃーん ◯ じゃじゃーん ◯ じゃじゃじゃじゃーん |
Pythonでの正規表現
では実際にPythonで正規表現でのパターンマッチをやってみましょう。
Pythonには「re」という正規表現用の標準モジュールが用意されています。
文字列の先端がマッチするかチェックする
文字列の先端が指定した正規表現にマッチしているかどうかをチェックするにはmatch()を使います。
import re
s1 = '123abc'
s2 = 'abc123'
m1 = re.match('[0-9]', s1)
print(m1)
m2 = re.match('[0-9]', s2)
print(m2)
[出力結果]
<re.Match object; span=(0, 1), match='1'>
None
「123abc」と「abc123」という2つの文字列に対して[0-9]という正規表現にマッチしているかどうかをチェックしています。
[0-9]は0〜9までの半角数字を表しているので、この処理は「先頭が半角数字」かどうかをチェックしていることになります。
「123abc」はマッチしています。マッチするとmatch()はMatchオブジェクトを返却します。このMatchオブジェクトからはマッチした文字列の開始位置や終了位置、またマッチした文字列自身を取得することができます(後述)。
また、マッチしなかった場合はNoneを返却します。単純にマッチしたかどうかをチェックしたいだけの場合は、match()の戻り値をそのままifに渡すだけです。
import re
def search_number(s):
if re.match('[0-9]', s):
print('見つかりました。')
else:
print('見つかりませんでした。')
s = '123'
search_number(s)
s = 'abc'
search_number(s)
[出力結果]
見つかりました。
見つかりませんでした。
文字列がマッチしているかチェックする
先頭にかかわらず、文字列が正規表現にマッチしているかどうかをチェックするにはsearch()を使います。
import re
s1 = '123abc'
s2 = 'abc123'
m1 = re.search('[0-9]', s1)
print(m1)
m2 = re.search('[0-9]', s2)
print(m2)
[出力結果]
<re.Match object; span=(0, 1), match='1'>
<re.Match object; span=(3, 4), match='1'>
match()と違い、どの位置でもマッチされるので、abc123でもマッチされました。
文字列全体がマッチするかチェックする
一部ではなく、文字列全体がマッチしているかどうかをチェックするにはfullmatch()を使います。
import re
s = '123abc'
m1 = re.fullmatch('[0-9a-z]+', s)
print(m1)
m2 = re.fullmatch('[0-9]+', s)
print(m2)
[出力結果]
<re.Match object; span=(0, 6), match='123abc'>
None
[0-9]+は「0〜9までの文字の1回以上の繰り返し」、[0-9a-z]+は「0〜9、a〜zまでの文字の1回以上の繰り返し」です。
fullmatch()は文字列全体がマッチしている必要があるので、[0-9a-z]+の方のみマッチしました。
マッチする文字列をリストで取得する
findall()を使うことで、マッチした文字列をリストで取得することができます。
import re
s = '123abc456def'
m_list = re.findall('[0-9]+', s)
print(m_list)
[出力結果]
['123', '456']
マッチした文字列を置き換える
マッチした文字列を別の文字列に置き換えたい場合はsub()が使えます。
import re
s = '123abc456def'
s_sub = re.sub('[a-z]', '0', s)
print(s_sub)
[出力結果]
123000456000
2つ目の引数に置き換える文字列、3つ目の引数に検索対象の文字列を指定します。
上記の例では「半角小文字」を「0」に置き換えています。
マッチオブジェクトから情報を取得する
match()やsearch()で検査した文字列がマッチしていた場合、戻り値としてMatchオブジェクトを返却します。
このMatchオブジェクト内にはマッチした位置や文字列といった情報が含まれています。
マッチした位置を取得する
マッチした文字列の位置を取得する場合はstart()、end()、span()を使います。
start()は開始位置、end()は終了位置、span()は開始位置と終了位置のタプルを返却します。
import re
s = '123abc456def'
m = re.search('[a-z]+', s)
print(m.start())
print(m.end())
print(m.span())
[出力結果]
3
6
(3, 6)
例から分かるように、位置はインデックス番号で返却されます。
マッチした文字列を取得する
マッチした文字列を取得するにはgroup()を使います。
import re
s = '123abc456def'
m = re.search('[a-z]+', s)
print(m.group())
[出力結果]
abc