Pythonで正規表現マッチング操作を行うには、標準組込みのreモジュールを使います。
本記事では、
- reモジュールを使った正規表現マッチング操作の基本
- 文字列の置き換えや分割等の文字列操作
について具体例を挙げてまとめていきます。
尚、正規表現パターンの作り方(書式)についてはこちらの記事でまとめていますのでご参照ください。
確認した環境
- OS: Ubuntu 20.04LTS@Windows 10 WSL2
- Python: ver3.9.0
正規表現とは?
ビギナーの方はこの言葉に馴染みがない方もいると思いますので、Wikipediaから抜粋します。
正規表現(せいきひょうげん、英: regular expression)とは、文字列の集合を一つの文字列で表現する方法の一つである。(中略)アプリケーションやプログラミングにおいて正規表現を用いた文字列のパターンマッチングを行う機能のことを、単に正規表現という。
reモジュールを使った正規表現マッチング操作
reモジュールを使った正規表現マッチング操作(=正規表現にマッチした文字列を取得する操作)の基本的な流れは以下です。
- reモジュールをインポートする
- 正規表現パターンを使って、正規表現オブジェクトを作る
- マッチオブジェクトを作る
- マッチング結果を出力
これをコード風に書くと下記のようになります。
# 1. reモジュールのインポート
import re
# 2. 正規表現オブジェクトを作る
regex = re.compile(正規表現パターン)
# 3. マッチオブジェクトを作る
mo = regex.search(検索対象)
# 4. マッチング結果を出力
mo.group()
正規表現オブジェクトを作る
compile()メソッドを使います。
使い方は下記です。
re.compile(pattern, flags=0)
検索したい文字列の正規表現パターンpatternを引数に設定します。返り値は正規表現オブジェクトです。
下記にいくつか例を示します。
# 特定の文字列を検索する場合(ここでは 'abc')
>>> regex = re.compile(r'abc')
# 正規表現パターンを設定する場合(0〜9の数字を検索する場合は、'\d')
>>> regex = re.compile(r'\d')
# (参考)型を確認
>>> type(regex)
<class 're.Pattern'> # 正規表現オブジェクト
正規表現パターンの作り方は下記をご参照ください。
参考記事)【Python】正規表現の表記方法のまとめ(reモジュール)
マッチオブジェクトを作る
上記で生成した正規表現オブジェクトre.Patternのsearch()メソッドは、正規表現パターンに最初に一致した(マッチした)文字列を探して、マッチオブジェクトを返します。
使い方は以下です。
Pattern.search(string[, pos[, endpos]])
引数はそれぞれ下記の通りです。
- 引数string: 検索対象の文字列
- 引数pos: 文字列のどこから探し始めるかインデックスを指定(デフォルトは 0)
- 引数endpos: 文字列がどこまで検索されるかを指定
具体的なコード例を示します。
>>> import re
# 特定の文字列を検索(ここでは 'abc')
>>> regex = re.compile(r'abc')
# 最初にマッチした文字列を探してマッチオブジェクトを返す
>>> mo = regex.search('abc123abc456')
>>> mo
<re.Match object; span=(0, 3), match='abc'>
引数pos, endposを設定した場合
# 検索対象の文字列を(3, 10)の範囲で検索
>>> mo = regex.search('abc123abc456', 3, 10)
>>> mo
<re.Match object; span=(6, 9), match='abc'>
マッチしたパターンが見つからない場合はNoneを返します
>>> mo = regex.search('123456')
>>> print(mo)
None
生成されたマッチオブジェクトを確認すると、文字列のどこでどの文字がマッチングしているのかがわかります。今回の例では、検索対象の文字列の[0:3]や[6:9]の箇所で、文字列’abc’でマッチングしています。
マッチング結果を出力する
マッチした文字列を返す
上記で生成したマッチオブジェクトre.Matchのgroup()は、グルーピングされたマッチング結果の文字列を返します。
使い方は以下です。
match.group([group1, ...])
表示するサブグループ名を引数group1,,,に設定します。デフォルト値は0で、マッチング結果を全て出力します。
具体例を以下に示します。
>>> import re
# グルーピングを使って正規表現パターンを生成
>>> regex = re.compile(r'(\d{3})-(\d{3})-(\d{3})')
# マッチオブジェクトを生成
>>> mo = regex.search('123-456-789')
# グルーピング名を省略 or 0を指定すると全部出力される
>>> mo.group()
'123-456-789'
>>> mo.group(0)
'123-456-789'
# グルーピング名をそれぞれ指定した場合
>>> mo.group(1)
'123'
>>> mo.group(2)
'456'
>>> mo.group(3)
'789'
マッチング箇所をインデックスで取得
生成したマッチオブジェクトに対しspan()を使うと、マッチング箇所のインデックスを2タプル(start, end)で返します。
使い方は以下です。
Match.span([group])
引数groupは上記同様にサブグループ名で、ここに設定したグループ名のインデックスが返ります。
省略または0を指定すると全部出力されます。
>>> import re
# グルーピングを使って正規表現パターンを生成
>>> regex = re.compile(r'(\d{3})-(\d{3})-(\d{3})')
# マッチオブジェクトを生成
>>> mo = regex.search('123-456-789')
# 引数を省略するか0の場合はマッチング結果全体を返します
>>> mo.span()
(0, 11)
>>> mo.span(0)
(0, 11)
# グルーピング名をそれぞれ指定した場合
>>> mo.span(1)
(0, 3)
>>> mo.span(2)
(4, 7)
>>> mo.span(3)
(8, 11)
尚、この動作はstr.find()とほぼ同様で、文字列の中で特定のキーワードを検索するのにも使えます。
こちらの記事もご参照ください。
参考記事)Pythonで文字列を検索する(in演算子、find、正規表現)
グルーピングについて
正規表現パターンの一部をカッコ”()”を使ってグルーピングすることができます。
各グループには名前が付けられており、デフォルトは1〜99の整数値です。
また、シンボリックグループ名(?P…)を使うと、文字列で表すこともできます。
# 3桁の数字がハイフン'-'で4つ繋がったパターンを検出する場合
>>> regex = re.compile(r'(\d{3})-(\d{3})-(\d{3})-(\d{3})')
# それぞれのグループ名を" ?P<...> "で設定
>>> regex = re.compile(r'(?P<area>\d{3})-(?P<local>\d{3})-(?P<id>\d{4})')
- グループ名をデフォルト(整数値)で指定する場合
# 市外局番を含む電話番号(3桁数字-3桁数字-4桁数字)の例
>>> regex = re.compile(r'(\d{3})-(\d{3})-(\d{4})')
>>> mo= regex.search('Phone number is 012-345-6789.')
# group()の引数(=グループ名)を省略すると全部出力
>>> mo.group()
'012-345-6789'
# グループ名=0の場合も全部出力
>>> mo.group(0)
'012-345-6789'
# グループ名=1のみを出力
>>> mo.group(1)
'012'
# グループ名を複数指定すると、タプルで出力
>>> mo.group(2,3)
('345', '6789')
# それぞれのグループ名を、?P<...>で設定
>>> regex = re.compile(r'(?P<area>\d{3})-(?P<local>\d{3})-(?P<id>\d{4})')
>>> mo= regex.search('Phone number is 012-345-6789.')
# グループ名を文字列で指定
>>> mo.group('area')
'012'
>>> mo.group('local', 'id')
('345', '6789')
# 整数値でも使えます
>>> mo.group(0)
'012-345-6789'
>>> mo.group(1)
'012'
マッチング結果を全て出力
上述したsearch()は最初のマッチング結果のみを抽出しましたが、マッチング結果を全て出力したい場合は、正規表現オブジェクトに対し、findall()を使います。
Pattern.findall(string[, pos[, endpos]])
引数はsearch()と同様ですが、返り値はマッチング結果をリストで出力します。
例を以下に示します。
>>> import re
>>> regex = re.compile(r'\d{3}')
# 結果はリストで出力されます
>>> regex.findall('123-456-789-abc')
['123', '456', '789']
また、次の様に正規表現オブジェクト生成(re.compile)を省略して、簡易的にre.findall()を使うこともできます。
re.findall(pattern, string, flags=0)
第一引数に正規表現パターンを設定する以外、第二引数以降は上記と同じです。
使用例を以下に記載します
>>> re.findall(r'\d{3}', '123-456-789-abc')
['123', '456', '789']
こちらの方が簡単に書けますが、同じ正規表現を何回も使う場合は、前記のように正規表現オブジェクトを保存して再利用するほうが効率的とのことです。(公式リファレンスより)
文字列の置き換え
正規表現オブジェクトに対してsub()を使うと、文字列の置き換えができます。
使い方は以下です。
regex.sub(repl, string, count=0)
返り値は、置き換え後の文字列です。
- 引数repl: 置き換える文字列
- 引数string: 元の文字列
- 引数count: 置き換えられるパターンの出現回数の最大値
※このパラメータが省略 or 0の場合は、マッチした文字列全てが置換されます。
具体的な例を以下に示します。
>>> regex = re.compile(r'\d{3}')
# countパラメータが省略 or 0の場合は全て置換
>>> regex.sub('***', '123-456-789-abc')
'***-***-***-abc'
# countパラメータを1以上の数値に設定した場合
>>> regex.sub('***', '123-456-789-abc', 1)
'***-456-789-abc'
>>> regex.sub('***', '123-456-789-abc', 2)
'***-***-789-abc'
>>> regex.sub('***', '123-456-789-abc', 3)
'***-***-***-abc'
また、次の様に正規表現オブジェクト生成(re.compile)を省略して、簡易的にre.sub()を使うこともできます。
re.sub(pattern, repl, string, count=0, flags=0)
第一引数に正規表現パターンを設定する以外、第二引数以降は上記と同じです。
使用例を以下に記載します
>>> re.sub(r'\d{3}', '***', '123-456-789-abc')
'***-***-***-abc'
>>> re.sub(r'\d{3}', '***', '123-456-789-abc', 1)
'***-456-789-abc'
>>> re.sub(r'\d{3}', '***', '123-456-789-abc', 2)
'***-***-789-abc'
>>> re.sub(r'\d{3}', '***', '123-456-789-abc', 3)
'***-***-***-abc'
マッチングする文字列を特定すると、str.replace()と同様な機能が得られます。
こちらの記事もご参照ください。
参考記事)【Python】任意の文字列を置換する3つの方法
文字列の分割
正規表現オブジェクトに対しsplit()を使うと、文字列を指定した区切り文字(文字列)で分割できます。分割された文字列がリストで返ります。
使い方は以下です。
regex.split(string, maxsplit=0)
引数はそれぞれ以下です。
- 第一引数string: 区切り文字(文字列)。この文字列で分割します。
- 第二引数maxsplit:分割数。例えばmaxsplit=1の場合は、1個の分割が発生(要素は2つ)し、残りの文字列は最終要素として返されます。尚、デフォルト値は0で、この場合は全て分割されます。
例を以下に示します。
>>> regex = re.compile(r'@')
# '@'で文字列を分割
>>> regex.split('abc.def@example@xyz.com')
['abc.def', 'example', 'xyz.com']
# maxsplit=1の場合は1回の分割
>>> regex.split('abc.def@example@xyz.com', 1)
['abc.def', 'example@xyz.com']
また、次の様に正規表現オブジェクト生成(re.compile)を省略して、簡易的にre.split()を使うこともできます。
re.split(pattern, string, maxsplit=0, flags=0)
第一引数に正規表現パターンを設定する以外、第二引数以降は上記と同じです。
使用例を以下に記載します。
>>> re.split(r'@', 'abc.def@example@xyz.com')
['abc.def', 'example', 'xyz.com']
マッチングする文字列を特定するとstr.split()メソッドと同様な機能が得られます。
以下の記事もご参照ください。
参考記事)【Python】split()を使った文字列の分割方法
まとめ
正規表現の使い方の基本について、
- reモジュールを使った正規表現オブジェクトの生成(compile()メソッド)
- Matchオブジェクト生成(search()オブジェクト)による抽出結果の出力方法
- 文字列の置き換え(sub()メソッド)
- 文字列の分割(split()メソッド)
についてまとめました。