Pythonのreモジュールによる正規表現のメモ
目次
- Pythonのreモジュールで正規表現を行う
- 正規表現のパターン
- 文字列の先頭にマッチさせるmatch
- マッチオブジェクト
- 最初にマッチする文字列を探すsearch
- すべてにマッチさせて結果を返すfindall
- マッチした文字列を置き換えるsub
- ハマりやすいところ(結果が期待した通りにならない)
- 改行の解析
- おわりに
Pythonのreモジュールで正規表現を行う
Pythonの正規表現はreモジュールを使うのが普通です。
この記事ではreモジュールの使い方を簡単にメモしておきます。
具体的にはmatch()
, group()
, search()
, findall()
, sub()
の使い方を紹介します。
関連記事
正規表現のパターン
正規表現で使われるパターン(.*
や\d
など)は↓の公式ドキュメントに詳しく書かれています。
↓で抜粋します。
パターン | 意味 | 例 |
\d |
数字にマッチ | \d+ |
\D |
数字意外にマッチ | \D+ |
\s |
空白文字 | |
\S |
空白文字以外 | |
\w |
英数字 | \w`+ |
\W |
英数字以外 | \W`+ |
\A |
文字列の先頭 | \A\d+ |
^ |
文字列の先頭 | ^\d+ |
\Z |
文字列の末尾 | \d+\Z |
$ |
文字列の末尾 | \d+$ |
. |
任意の1文字 | .* |
* |
繰り返し(0回以上) | .* |
+ |
繰り返し(1回以上) | .+ |
? |
0または1回 | \d?\d\d |
{m} |
m回の繰り返し | A{2} |
{m,n} |
mからn回の繰り返し | A{2,4} |
[] |
集合 | [a-zA-Z] |
| |
OR (または) | \d+|\w+ |
() |
グループ | (a-z+|A-Z+) |
文字列の先頭にマッチさせるmatch
matchは文字列の先頭にマッチさせます。
パターンにマッチした文字列を結果として取得します。
結果はマッチオブジェクトで返します。
パターンにマッチしなかった場合はNoneを返します。
import re target = r'aaa bbb aaa' pattern = 'a+' m = re.match(pattern, target) if m: # None以外ならパターンにマッチ print(m) # <re.Match object; span=(0, 3), match='aaa'> print(m.group()) # aaa # ↑の'aaa'は先頭の'aaa'
マッチオブジェクト
マッチオブジェクト(re.Match
)は常にif文ではTrueになります。
マッチオブジェクトにはメソッドがいくつかあります。
groupでマッチしたグループを得る
group()
はマッチの1つ以上のサブグループを返すメソッドです。
マッチした文字列を得たい場合はこのgroup()
で結果を得ます。
group()
はgroup(0)
と同じです。
これはマッチした全体を文字列で返します。
group(1)
はマッチした1つ目のグループの文字列を返します。
import re m = re.match(r'(\w+) (\w+)', 'Tanaka Taro, Ohayo') print(m.group()) # Tanaka Taro print(m.group(0)) # Tanaka Taro print(m.group(1)) # Tanaka print(m.group(2)) # Taro
↑の例ではパターンは「(\w+) (\w+)
」です。
これは「Tanaka Taro」のような文字列にマッチします。
()
がグループで、\w+
を囲っているのがわかります。
こうするとそのパターン(\w+
)がグループになります。
m.group()
ではマッチした「Tanaka Taro」全体が文字列として返ります。
m.group(1)
ではマッチした「Tanaka Taro」の内、「Tanaka」が返ります。
この「Tanaka」はパターン「(\w+) (\w+)
」の1つ目の(\w+)
にマッチしている部分です。
お察しのとおりm.group(2)
では「Taro」が返ります。
マッチした文字列にグループ名を付ける
group()
で取得できる文字列には名前を付けることができます。
↓のように書くと1つ目の(\w+)
にはone
という名前が。
2つ目の(\w+)
にはtwo
という名前が付きます。
import re m = re.match(r'(?P<one>\w+) (?P<two>\w+)', 'Tanaka Taro, Ohayo') print(m.group('one')) # Tanaka print(m.group('two')) # Taro
group(名前)
でその名前の文字列を取得できます。
最初にマッチする文字列を探すsearch
match()
は先頭にマッチさせるメソッドでした。
いっぽうsearch()
は先頭以外にもマッチして、最初にマッチした文字列を保存します。
同様にマッチしたらマッチオブジェクトを返します。
import re m = re.search(r'(\d+) (\d+)', 'aaa 123 456') print(m.group()) # 123 456
すべてにマッチさせて結果を返すfindall
findall()
はマッチしたすべての文字列をリストで返します。
import re li = re.findall(r'(\w+)=(\d+)', 'abc def=123 ghi jkl=223') print(li) # [('def', '123'), ('jkl', '223')]
↑の「(\w+)=(\d+)
」というパターンは「def=123
」のような文字列にマッチします。
このような文字列は他にjkl=223
がありますが、findall()
はこれらすべてにマッチして結果を返します。
一般的によくつかうのはfindall()
だと思います。
マッチした文字列を置き換えるsub
sub()
は第1引数のパターンにマッチする文字列を第2引数で置き換えます。
第3引数には対象の文字列を渡します。
import re result = re.sub(r'\d+', '***', 'abc 123 def 223') print(result) # abc *** def ***
subを使ってノイズを消す
sub
を使うと不要な文字列を消す前処理が書けます。
たとえばHTMLからマッチして文字列を探したい場合です。
import re html = '''<ul> <li><span>123</span></li> <li><span>456</span></li> <li><span>789</span></li> </ul> ''' s = re.sub(r'</?span>', '', html) li = re.findall('<li>(.*)</li>', s) print(li) # ['123', '456', '789']
↑の例では<li>
タグの中にある<span>
タグをre.sub()
で消して前処理をしています。
そのあとにfindall()
を使って<li>
タグの中身を取得しています。
ハマりやすいところ(結果が期待した通りにならない)
パターンの「.*
」はなるべく多い文字列でマッチしようとします。
いっぽうこれと違い少ない文字でマッチしようとするのが「.*?
」です。
たとえば↓のようなケースを見てください。
import re m = re.match(r'.*(\d+)', 'abc123') print(m.group()) # abc123 print(m.group(1)) # 3 m = re.match(r'.*?(\d+)', 'abc123') print(m.group()) # abc123 print(m.group(1)) # 123
↑の2つのパターンで結果が違うのがわかります。
1つ目の「.*(\d+)
」ではgroup(1)
の結果が3
になってます。
2つ目の「.*?(\d+)
」ではgroup(1)
の結果は123
です。
期待する結果は多くは2つ目の結果だと思います。
普段から「.*?
」の方を使うクセを付けておくといいかもしれません。
改行の解析
match()
やsearch()
やfindall()
などでパターン「.*
」を使う場合、デフォルトでは改行を識別しません。
識別させたい場合はre.S
を第3引数に渡します。
import re m = re.match(r'.*', '123\n223\n323') print(m.group()) # 123 m = re.match(r'.*', '123\n223\n323', re.S) print(m.group()) # 123 # 223 # 323
おわりに
今回はPythonのreモジュールの正規表現を見てみました。
正規表現は非常に強力です。
使いこなせるようになるとプログラミングでの財産となるでしょう。
(^ _ ^) | 正規表現でマッチング |
(・ v ・) | マッチです |