Python: 正規表現 まとめ

Pythonでの正規表現を使った検索、置換、分割、最長マッチ、最短マッチ、特殊文字などに ついてまとめています。

正規表現で検索 search()

Pythonで正規表現を使って検索する場合、reモジュールの search()関数を使うとパターンにマッチするかどうかがわかります。

書式
search(パターン, 検索対象文字列 [, flags=0])

マッチする場合はマッチ情報が格納されたmatchオブジェクトが戻り、 マッチしない場合は「None」が戻ります。

マッチする場合

マッチする場合、どのような情報が取得できるのかを見てみます。

次のコードは変数「p」がパターン文字列、変数「s」が検索対象文字列です。 HTMLコードの中から b タグがあるかどうかを検索します。
(「imoprt re」を忘れずに記述してください)

import re

p = "<b>.+</b>"
s = "<div><b>hello</b></div>"

# 正規表現で検索
m = re.search(p, s)

print(m)
実行結果
<re.Match object; span=(5, 17), match='<b>hello</b>'>

マッチした場合はこのように、 match オブジェクトの内容が表示されます。

マッチしない場合

次はマッチしない場合を見てみます。

HTMLコードの中から i タグがあるかどうかを検索します。

import re

p = "<i>.+</i>"
s = "<div><b>hello</b></div>"

# 正規表現で検索
m = re.search(p, s)

print(m)
実行結果
None

マッチしない場合は「None」と表示されます。

マッチする・しないで処理を分岐させる

if文を使って、 マッチする・しないで処理を分岐させる場合は次のように記述できます。

import re

p = "<b>.+</b>"
s = "<div><b>hello</b></div>"

# 正規表現で検索
m = re.search(p, s)

if m:
	# マッチする場合
	print(m)
else:
	# マッチしない場合
	print(m)

マッチした文字列を取得する

matchオブジェクトからマッチした文字列だけを取得したい場合は、 matchオブジェクトの group()メソッドを使います。

import re

p = "<b>.+</b>"
s = "<div><b>hello</b></div>"

# 正規表現で検索
m = re.search(p, s)

if m:
	# マッチする場合
	print(m.group())
else:
	# マッチしない場合
	print(m)

「matchオブジェクト.group()」でマッチした文字列が取得できます。

実行結果
<b>hello</b>

正規表現で検索(先頭のみ) match()

正規表現を使って検索する場合、reモジュールの match()関数を使うと、 先頭の文字列がパターンにマッチするかどうかがわかります。

search()関数は任意の位置のマッチングを調べますが、 match()関数は先頭のみのマッチングを調べます。

書式
match(パターン, 検索対象文字列 [, flags=0])

マッチする場合はマッチ情報が格納されたmatchオブジェクトが戻り、 マッチしない場合は「None」が戻ります。

マッチする場合

具体的にmatch()関数を使って先頭検索を行います。
次のコードは変数「p」がパターン文字列、変数「s」が検索対象文字列です。 先頭が「c.t」のパターンに一致するかどうかを検索します。 (「imoprt re」を忘れずに記述してください)

import re

p = "c.t"
s = "cut cup cute"

m = re.match(p, s)

print(m)
実行結果
<re.Match object; span=(0, 3), match='cut'>

マッチした場合はこのように、 match オブジェクトの内容が表示されます。

マッチしない場合

次はマッチしない場合を見てみます。
先頭が「b.t」のパターンに一致するかどうかを検索します。

import re

p = "b.t"
s = "cut cup cute"

m = re.match(p, s)

print(m)
実行結果
None

マッチしない場合は「None」と表示されます。

マッチする・しないで処理を分岐させる

if文を使って、 マッチする・しないで処理を分岐させる場合は次のように記述できます。

import re

p = "b.t"
s = "cut cup cute"

m = re.match(p, s)

if m:
	print(m)
else:
	print(m)

マッチした文字列を取得する

matchオブジェクトからマッチした文字列だけを取得したい場合は、 matchオブジェクトの group()メソッドを使います。

import re

p = "c.t"
s = "cut cup cute"

m = re.match(p, s)

if m:
	print(m.group())
else:
	print(m)

「matchオブジェクト.group()」でマッチした文字列が取得できます。

実行結果
cut

マッチした文字列を取得する group()

Pythonの正規表現を使った検索で、 search()関数やmatch()関数でパターンにマッチした場合は、 戻り値として matchオブジェクトが戻ってきます。

そこからマッチした文字列を取得する場合は、 matchオブジェクトの group()メソッドを使います。

書式
matchオブジェクト.group([グループ番号])

戻り値はマッチした文字列です。引数を指定しないか0を指定した場合は マッチした全体の文字列を返します。

グループ番号を指定しない場合 group()

パターン指定において、()丸括弧によるグループ化をしていない場合は、 group()メソッドの引数にグループ番号を指定する必要はありません。 (指定するとエラーが発生します)

import re

p = "<b>.+</b>"
s = "<div><b>hello</b></div>"

# 正規表現で検索
m = re.search(p, s)

if m:
	# matchオブジェクト
	print(m)

	# マッチした文字列
	print(m.group()) # <b>hello</b>

最初の「print(m)」でmatchオブジェクトの内容を出力して、 2番目の「print(m.group())」でマッチした文字列を出力しています。 違いを実行結果で確認してください。

実行結果
<re.Match object; span=(5, 17), match='<b>hello</b>'>
<b>hello</b>

グループ番号を指定する場合 group(n)

次のコードでは、パターン指定時に()丸括弧を使ってグループ化しています。 ですからグループ番号を指定することができます。

import re

p = "<b>(.+?)</b><b>(.*?)</b>"
s = "<div><b>hello</b><b>world</b></div>"

# 正規表現で検索
m = re.search(p, s)

if m:
	# matchオブジェクト
	print(m)

	# マッチした文字列
	print(m.group())  # <b>hello</b><b>world</b>
	print(m.group(1)) # hello
	print(m.group(2)) # world

それぞれのprint()関数において、どのような内容が出力されるのかを 実行結果で確認してください。

実行結果
<re.Match object; span=(5, 29), match='<b>hello</b><b>world</b>'>
<b>hello</b><b>world</b>
hello
world

最長マッチ・最短マッチ

Pythonの正規表現における 「最長マッチ」と「最短マッチ」の違いや、 それぞれの指定方法を紹介します。

最長マッチ

正規表現の繰り返しを表す特殊文字として「*」「+」「?」があります。 これらで記述したパターンにマッチする範囲は、一番長い範囲です。

例えば次のようなHTMLを正規表現で検索してみます。 (div 要素の中に2つの b 要素があります)

import re

# パターン
p = "<b>.*</b>"

# 検索対象文字列
s = "<div><b>hello</b><b>world</b></div>"

m = re.search(p, s)

if m:
	print(m.group())

パターンを「<b>.*</b>」とした場合の実行結果は次の通りです。

実行結果
<b>hello</b><b>world</b>

実行結果をみると、「<b>hello</b>」ではなく、 「<b>hello</b><b>world</b>」となっています。 最も長い範囲が取得されていて、これを「最長マッチ」といいます。

最短マッチ

「最長マッチ」に対して最も短い範囲で取得する場合を「最短マッチ」といいます。 上のコードだと「<b>hello</b>」が最短マッチとなります。

「最短マッチ」で取得する場合は、繰り返しを表す特殊文字「*」「+」「?」のあとに 「?」を付けます。
つまり「*?」「+?」「??」と記述します。

import re

# パターン
p = "<b>.*?</b>"

# 検索対象文字列
s = "<div><b>hello</b><b>world</b></div>"

m = re.search(p, s)

if m:
	print(m.group())

今回はパターンを「<b>.*?</b>」としています。 実行結果は次の通りです。

実行結果
<b>hello</b>

最も短い範囲の bタグが取得できています。
「最短マッチ」で取得したい場合は、 「*?」「+?」「??」のように、後ろに「?」を付けてください。

マッチ文字列をリストで取得 findall()

マッチしたすべての文字列をリストとして取得するには、 reモジュールのfindall()関数を使います。

書式
findall(パターン, 検索対象文字列 [, flags=0])

戻り値はリストで、マッチしたすべての文字列が各要素に入っています。 マッチしなかった場合は空のリストが戻ります。

次のコードでは、HTMLの li 要素の内容を取得して リストとして受け取ります。

import re

# パターン
p = "<li>(.*?)</li>"

# 検索対象文字列
s = """<ul>
<li>リストA</li>
<li>リストB</li>
<li>リストC</li>
</ul>
"""

# 検索
li = re.findall(p, s)

print(li)
実行結果
['リストA', 'リストB', 'リストC']

このように、 findall()関数を実行するだけでマッチ内容がリストとして取得できます。 この後に何かしらの処理をする場合も扱いやすいので、 複数マッチする場合は findall()関数がおすすめです。

マッチ文字列をイテレータで取得 finditer()

マッチ部分をイテレータとして取得する場合は、 finditer()関数を使います。

マッチ部分をリストとして取得するにはfindall()関数を使いますが、 finditer()はイテラブルなオブジェクトとして取得できます。

書式
finditer(パターン, 検索対象文字列 [, flags=0])

戻り値は「matchオブジェクト」を要素とするイテレータです。

次のコードは、 HTMLの li 要素のテキスト部分をfinditer()関数を使って取得します。

import re

# パターン
p = "<li>(.*?)</li>"

# 検索対象文字列
s = """<ul>
<li>リストA</li>
<li>リストB</li>
<li>リストC</li>
</ul>
"""

# 検索
itr = re.finditer(p, s)

print(itr)

for m in itr:
	print(m)
	print(m.group(1))

「print(m)」の表示結果で、 各要素にmatchオブジェクトが格納されていることがわかります。
「print(m.group(1)」で li 要素のテキスト部分のみを表示しています。

実行結果
<callable_iterator object at 0x000002C0BBCEF088>
<re.Match object; span=(5, 18), match='<li>リストA</li>'>
リストA
<re.Match object; span=(19, 32), match='<li>リストB</li>'>
リストB
<re.Match object; span=(33, 46), match='<li>リストC</li>'>
リストC

正規表現による置換 re.sub()

正規表現による置換処理を行う場合は、 reモジュールの sub() 関数を使います。

書式
sub(パターン, 置換後文字列, 検索対象文字列, count=0, flags=0)

戻り値は置換された後の文字列です。
countを省略するか「0」を指定した場合は、マッチしたすべてが置換されます。

次のコードは、パターン「c.t」にマッチする文字列を「xxx」に置換します。

import re

p = "c.t"

s = "cat hat cut hit cup"

s_new = re.sub(p, "xxx", s)

print(s_new) # xxx hat xxx hit cup

re.sub()で改行コードを削除

次は re.sub()を使って改行コードを削除するコードです。 置換後の文字列に「""」空文字を指定することで、削除になります。

改行コードは「\r\n」「\n」のどちらにも対応できるように、 パターンを「\r?\n」とします。

import re

# パターン
p = "\r?\n"

s = "a\nbcd\r\nefg\r\nhi\njkl"

s_new = re.sub(p, "", s)

print(s_new) # abcdefghijkl

正規表現による分割 re.split()

正規表現による文字列の分割処理を行う場合は、 reモジュールの split() 関数を使います。

書式
split(パターン, 検索対象文字列, maxsplit=0, flags=0)

戻り値はパターンによって分割された文字列のリストです。 パターンマッチしない場合は、 要素数1つのリストとして検索対象文字列がそのまま返って来ます。

次のコードは改行コードで文字列を分割します。
「\r\n」「\n」のどちらにも対応できるようにパターンを「\r?\n」としています。

import re

# パターン
p = "\r?\n"

s = "ab\ncd\r\nef\r\ngh\nij"

# 正規表現で分割
li = re.split(p, s)

print(li)

改行で分割された文字がリストとして取得できています。

実行結果
['ab', 'cd', 'ef', 'gh', 'ij']

maxsplitを指定した場合

上と同じコードで、今度は maxsplit(最大分割数)を指定してみます。

import re

# パターン
p = "\r?\n"

s = "ab\ncd\r\nef\r\ngh\nij"

li = re.split(p, s, maxsplit=2)

print(li)
実行結果
['ab', 'cd', 'ef\r\ngh\nij']

「maxsplit=2」を指定した場合は、 最初の2つの要素にパターンで分割された文字列が入り、 残りの文字列は3番目の要素にまとめて入っています。

パターンをコンパイルする compile()

Pythonでは正規表現のパターンを何度も使いまわす場合、 compile()関数であらかじめパターンをコンパイルしておくことができます。

コンパイルしておくことで、正規表現での検索が高速に実行されます。

書式
compile(パターン, flags=0)

戻り値はregexオブジェクトです。「コンパイル済み正規表現オブジェクト」とも言います。

regexオブジェクトのメソッドとして、 search()、match()、findall()、finditer()、sub()、split()などがあります。
例えばregexオブジェクトのsearch()メソッドは、 モジュールレベルのsearch()関数と同じ働きをします。 その他のメソッドも同じです

ではパターンをコンパイルしない場合と、 コンパイルする場合のコードを記述して、違いを見てみます。

パターンをコンパイルしない場合

下はモジュールレベルの search()関数を使ったコードです。 b タグのテキスト部分を取得しています。

import re

p = "<b>(.+)</b>"
s = "<div><b>hello</b></div>"

# search()関数
m = re.search(p, s)

print(m.group(1)) # hello;

パターンをコンパイルする場合

下のコードでは、 まずパターンをコンパイルしてregexオブジェクトを取得しています。 次にregexオブジェクトのsearch()メソッドでマッチングしています。

p = "<b>(.+)</b>"
s = "<div><b>hello</b></div>"

# コンパイル
reg = re.compile(p)

# search()メソッド
m = reg.search(s)

print(m.group(1)) # hello;

同じパターンを使いまわす場合は、 コンパイルした方が高速なのでおすすめです。

特殊文字一覧

任意の1文字

特殊文字説明
.(ドット)任意の1文字

行頭・行末

特殊文字説明
^行頭
$行末

繰り返し

繰り返しは「直前の文字を繰り返す」ことを意味します。

特殊文字説明
*0回以上
+1回以上
?0回または1回以上
{n}n回
{n,}n回以上
{n,m}n回~m回

「最長マッチ」と「最短マッチ」

「*」「+」「?」は最長マッチになります。 最短マッチで取得する場合は「*?」「+?」「??」のように 後ろに「?」を付けます。

(正規表現の最長マッチと最短マッチ)

文字クラス

[]角括弧で括ったパターンは「文字クラス」と言います。 []内のいずれかの文字に一致するかどうかを調べます。
特殊文字説明
[文字の並び]文字の並びのいずれかの文字
[^文字の並び]文字の並び以外の文字

文字クラスの具体的なパターンの例を紹介します。

パターン説明
[abc]abcのいずれか
[0-9]0~9のいずれか
[A-Z]A~Zのいずれか
[A-Za-z]A~Z,a~zのいずれか
[^0~9]0~9以外の文字

文字クラスの簡易表現

文字クラスでよく使うパターンの簡易表現です。

特殊文字説明
\s空白
\S空白以外
\d数字
\D数字以外
\w英数字、[A-Za-z0-9]と同じ
\W英数字以外
\A文字列の先頭
\Z文字列の末尾

グループ化と選択

特殊文字説明
()グループ化
|いずれか

その他

特殊文字説明
\b単語の境界
\\\

検索フラグ flags

Pythonの正規表現で使うreモジュールのsearch()関数、match()関数、findall()関数 や、コンパイル済み正規表現オブジェクトのsearch()メソッド、match()メソッド、 findall()メソッドなどで、指定できる引数の1つに「flags」があります。

search(パターン, 検索対象文字列 [, flags=0])

match(パターン, 検索対象文字列 [, flags=0])

findall(パターン, 検索対象文字列 [, flags=0])

デフォルト値は「flags=0」となっており、何も指定されていませんが、 少し複雑な正規表現の場合は flags を使用するケースがあります。

ここでは主なフラグの値を説明します。

フラグ説明
re.ASCIIASCII限定のマッチング
re.IGNORECASE大文字小文字を区別する
re.MULTILINE「^」が各行の先頭にマッチし、「$」が各行の行末にマッチする
re.DOTALL特殊文字「.」を改行を含むあらゆる文字に対応させる。 指定がない場合は改行以外のあらゆる文字とマッチする。
re.VERBOSEパターンにコメントを加えたり、複数行で記述できる

この中でも押さえておきたいのは 「re.IGNORECASE」「re.MULTILINE」「re.DOTALL」の3つです。

フラグの指定方法

フラグを1つ指定する場合は次のように指定します。

書式
m = re.search(パターン, 検索対象文字列, flags=re.DOTALL)

フラグを複数指定する場合は「|」でつなぎます。

書式
m = re.search(p, s, flags=re.DOTALL | re.IGNORECASE)

「re.DOTALL」を使うケース

次のコードはパターン「<p>.*</p>」を指定して、 pタグで囲まれた文字列を取得しようとしてますが、 結果として「None」になっています。

import re

# パターン
p = "<p>.*</p>"

s = """<p>hello
world</p>
"""

m = re.search(p, s)

print(m) # None

検索対象文字列が複数行になっているためうまく取得できませんでした。

複数行に渡る文字列を取得する場合は、 改行にもマッチさせるために「re.DOTALL」を指定します。

import re

# パターン
p = "<p>.*</p>"

s = """<p>hello
world</p>
"""

m = re.search(p, s, flags=re.DOTALL)

print(m.group())

今回はpタグを取得できています。

実行結果
<p>hello
world</p>