Pythonの正規表現、最初は難しいと感じてしまう方が多いと思います。正規表現をうまく扱えるようになると、フォームに入力された内容のチェックや文字の検索・置換が非常に簡単に出来るようになります。機械学習の代表例でもある自然言語処理も、この正規表現で行うことが可能となります。
ここでは「正規表現って何?」「正規表現の関数にはどんなものがあるの?」「Pythonで正規表現を使うにはどうすれば良いの?」といった方へ、Pythonで正規表現を使う方法について説明していきます。
YouTubeも公開していますので、動画や音声で聞きたい方はぜひご覧ください。
Pythonの正規表現とは
正規表現とは、文字列のパターンに対して一致するかどうかをチェックするための処理方法の一つです。プログラミングを学習していれば避けては通れない入門的なもの関わらず、初めて見た人は暗号のようなとても難解なものに見えてゲンナリするのが正規表現です。
ですが、大体のことを覚えてしまえばそこまで手強い相手でもありません。正規表現では「メタ文字」という記号を組み合わせて文字列のパターンを検出しますが、メタ文字のパターンはそこまで多くありません。
メタ文字一覧
メタ文字は以下の通りです。このメタ文字を組み合わせて文字列のパターンマッチをするのが正規表現の基本的な使い方となります。いきなり見ると沢山あるように思われますが、使っているうちに自然に覚えていくでしょう。
メタ文字 | 意味 | 例 | 検索できる文字列の例 |
---|---|---|---|
. | 何かしらの一文字 | 私は.です。 | ◯ 私は人です。 ◯ 私は犬です。 ◯ 私は鳥です。 |
^ | 行の先頭 | ^こんにちは | × 私はこんにちはと挨拶した ◯ こんにちはと挨拶したのは彼女 × あなたはこんにちはと挨拶した |
$ | 行の末尾 | さようなら$ | × 私はさようならを言った × さようならをしたあなた ◯ 別れの言葉はさようなら |
* | 直前の文字の0個以上の繰り返し | はー*い | ◯ はい ◯ はーい ◯ はーーーーーい |
+ | 直前の文字の1個以上の繰り返し | はー+い | × はい ◯ はーい ◯ はーーーーーい |
? | 直前の文字が0個か1個 | はー?い | ◯ はい ◯ はーい × はーーーーーい |
| | いずれかの文字列 | CD|DVD|Blu-ray | ◯ これはCDです。 ◯ これはDVDです。 ◯ これはBlu-rayです。 × これはHDDです。 |
[] | 指定した文字のどれか | [あいうえお] | ◯ あおまきがみあかまきがみきまきがみ ◯ となりのきゃくはよくかきくうきゃくだ × なまむぎなまごめなまたまご |
() | グループ化 | (じゃ)+ーん | ◯ じゃーん ◯ じゃじゃーん ◯ じゃじゃじゃじゃーん |
Pythonで正規表現を使うには
では、実際にPythonで正規表現でのパターンマッチをやってみましょう。
reモジュール
Pythonには「re」という正規表現用の標準モジュールが用意されています。本稿ではreを使ってコードを説明していきますので、最初にimport reでこのモジュールをimportしてください。今回インポートの部分は割愛しますが、モジュールとそのインポート方法については以下記事で解説していますので、やり方が分からない方は以下を参照してみてください。
文字列の先頭がマッチするか判定する
文字列の先頭が指定した正規表現にマッチしているかどうかを判定するには、match関数を使用します。書き方はmatch()です。
match関数の例を見てみましょう。以下のコードでは「123abc」と「abc123」という2つの文字列に対して、先頭が[0-9]という正規表現にマッチしているかどうかをチェックしています。
[サンプルコード]
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
[0-9]は0〜9までの半角数字を表しているので、この処理は「先頭が半角数字」かどうかを判定していることになります。
「123abc」はマッチしています。マッチするとmatch()はMatchオブジェクトを返却します。このMatchオブジェクトからはマッチした文字列の開始位置や終了位置、またマッチした文字列自身を取得することができます(後述)。
また、マッチしなかった場合はNoneを返却します。「abc123」は先頭が半角数字では無いので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関数を使います。書き方はsearch()です。
match関数で使用した文字列を再掲し、今度は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関数を使います。書き方はfullmatch()です。文字列「123abc」で例を見てみましょう。
[サンプルコード]
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]+の方のみマッチしました。
マッチする文字列をリストで取得する
先ほどのfullmatch関数では、マッチした文字列が具体的にどれなのか分かりませんでした。マッチした文字列をリストで取得するにはfindall関数を使います。文字列「123abc456def」で、先ほどの[0-9]+がマッチするか、マッチした場合どの部分が出力されるか試してみましょう。
[サンプルコード]
import re
s = '123abc456def'
m_list = re.findall('[0-9]+', s)
print(m_list)
[出力結果]
['123', '456']
マッチした文字列を置き換える
マッチした文字列を別の文字列に置き換えたい場合はsub関数が使えます。書き方はsub()です。
以下のサンプルコードの2行目でsub関数を実行していますが、今回は2つ目の引数に置き換える文字列を、3つ目の引数に検索対象の文字列を指定しています。
今回はマッチした文字列のうち「半角小文字」を「0」に置き換える処理をしてみました。
import re
s = '123abc456def'
s_sub = re.sub('[a-z]', '0', s)
print(s_sub)
[出力結果]
123000456000
文字列「123abc456def」では文字列全体がマッチしたため、そのうち「半角小文字」である「abc」が丸ごと置き換わりました。
マッチオブジェクトから情報を取得する
match関数やsearch関数で判定した文字列がマッチしていた場合、戻り値としてMatchオブジェクトを返却します。
このMatchオブジェクト内にはマッチした位置や文字列といった情報が含まれています。本稿の最後に、それぞれの情報を取得するメソッドを4種類紹介しようと思います。
マッチした位置を取得する
マッチした文字列の位置を取得する場合はstartメソッド、endメソッド、spanメソッドを使います。
それぞれstart()は開始位置、end()は終了位置、span()は開始位置と終了位置のタプルを返却するものです。「123abc456def」を例に、それぞれの位置情報を取得してみましょう。
[サンプルコード]
import re
s = '123abc456def'
m = re.search('[a-z]+', s)
print(m.start())
print(m.end())
print(m.span())
[出力結果]
3
6
(3, 6)
例から分かるように、位置はインデックス番号で返却されます。
マッチした文字列を取得する
マッチした文字列を取得するにはgroupメソッド(書き方:group())を使います。
[サンプルコード]
import re
s = '123abc456def'
m = re.search('[a-z]+', s)
print(m.group())
[出力結果]
abc
マッチオブジェクトから情報を取得する
最後に…正規表現には色々なメリットが
今回紹介した正規表現の書き方は基本的なものでしたが、冒頭でも述べたように自然言語処理や入力項目の正誤判別、大量の文字情報からの検索や置換など、Pythonを業務で使う上で様々な活用方法が考えられます。
非プログラマーの業務効率化にも大いに役立ちますので色々試してみてください!