Pythonのreモジュールによる正規表現のメモ
- 作成日: 2022-05-26
- 更新日: 2023-12-25
- カテゴリ: Python
Pythonのreモジュールで正規表現を行う
Pythonの正規表現はreモジュールを使うのが普通です。
この記事ではreモジュールの使い方を簡単にメモしておきます。
具体的にはmatch()
, group()
, search()
, findall()
, sub()
の使い方を紹介します。
関連記事
頭が悪い人のPythonのevalの使い方
頭がいい人のPythonのexitの使い方
解決済み日本語記事: get on proxy: property modelViewMatrix is a read-only
自作関数read_fileでファイルを読み込み【ファイル入出力, コマンド】
状態遷移による文字列パースのテクニック【Python】
正規表現のパターン
正規表現で使われるパターン(.*
や\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モジュールの正規表現を見てみました。
正規表現は非常に強力です。
使いこなせるようになるとプログラミングでの財産となるでしょう。
🦝 < 正規表現でマッチング
🐭 < マッチです