プログラミング

正規表現のreモジュールの関数(Python)

Pythonのreモジュールには、正規表現を使うための関数が複数用意されています。マッチした部分を取り出す関数、置換する関数、分割する関数、パターンを使い回すための関数など、目的ごとに使い分けます。

たとえば「文字列の中から数字だけを取り出す」なら、最初の1件だけでよいのか、全件ほしいのかで使う関数が変わります。最初の1件はre.search、全件をリストで取るならre.findall、1件ずつ位置情報も欲しいならre.finditer、という具合です。似た関数がいくつもあるので、それぞれの違いを整理します。

本記事では、reモジュールでよく使う関数を、Pythonのreモジュールで一つずつ整理します。reモジュールは標準モジュールなのでインストールは不要です。

なお、本記事で使う正規表現の基本構文・グループ・フラグは過去の記事で扱っています。

動作確認

Python3.12で実施しています。reモジュールを使用するため、import reを冒頭に入れてください。

目次

関数の一覧

本記事で扱う関数を先に一覧で示します。用途で3つに分けています。

■ マッチした部分を取り出す(読み取り)

関数戻り値主な用途
re.search最初のMatch/Noneどこかに一致する部分があるか探す
re.match文字列先頭からのMatch/None文字列の先頭からパターンに一致するか調べる
re.fullmatch文字列全体のMatch/None文字列全体がパターンに一致するか調べる
re.findall一致した部分のリスト全ての一致する箇所を一気にリストで取る
re.finditerMatchのイテレータ全ての一致する箇所を、位置情報付きで1件ずつ処理

■ 置換・分割(書き換え)

関数戻り値主な用途
re.sub置換後の文字列一致した部分を別の文字列に置き換える
re.subn(置換後の文字列, 置換回数)置換に加えてその回数も知りたい場合
re.split分割後の文字列のリストパターンにマッチする位置で文字列を分割する

■ 補助

関数戻り値主な用途
re.compilePatternオブジェクト同じパターンを何度も使うときに事前コンパイルする
re.escapeエスケープ済みの文字列記号を含む文字列をリテラルとしてマッチさせる

re.searchre.matchre.fullmatchが返すMatchオブジェクトは、パターンに一致した部分を表すオブジェクトです。.group()で一致した文字列、.span()で位置(開始と終了のインデックス)、キャプチャグループがあれば.group(1).group("名前")で個別の値を取り出せます。グループの詳細はこちらの記事を参照ください。

なお、re.escapeを除くすべての関数は、オプションで末尾にflags引数を取ります。フラグ(re.Iなど)を、どの関数でも共通の形で渡せます。基本の呼び出し方はre.関数(パターン, 文字列, flags=re.I)です。フラグの詳細はこちらの記事を参照ください。

re.subre.subncountre.splitmaxsplitという別のオプション引数を持ちますが、flags=re.Iのようにキーワード引数で明示すれば、これらの関数でも同じ形で渡せます。

マッチした部分を取り出す

マッチした部分を読み取る関数を順に見ていきます。

最初の1件を探す(re.search

re.searchは、文字列の中でパターンに一致する最初の部分を探し、見つかればMatchオブジェクト、なければNoneを返します。

m = re.search(r"\d+", "商品コード A123、在庫 45個")
print(m.group())   # '123'
print(m.span())    # (7, 10)
print(re.search(r"\d+", "no digits"))   # None

\d+(1個以上の数字)を先頭から探し、最初に見つかった123が返ります。Matchオブジェクトからは.group()で一致した文字列、.span()で位置が取れます(グループ編参照)。一致がなければNoneなので、if文で有無を判定できます。

先頭から一致するかを見る(re.match

re.matchは、文字列の先頭からパターンに一致するかを見ます。先頭にあればMatchオブジェクト、そうでなければNoneです。

print(re.match(r"\d+", "123abc"))    # <re.Match object; span=(0, 3), match='123'>
print(re.match(r"\d+", "abc123"))    # None

123abcは先頭が数字なので一致します。abc123は数字がありますが、先頭ではないのでNoneです。

なお、re.matchre.MULTILINEを付けても各行の先頭は見ず、文字列の先頭でしか照合しません。

print(re.match(r"banana", "apple\nbanana", re.MULTILINE))   # None

先頭から一致する必要がない場合は、re.searchの方が使いやすい場面が多くなります。

文字列全体が一致するかを見る(re.fullmatch

re.fullmatchは、文字列全体がパターンに一致するかを見ます。一部だけ一致していてもNoneになります。

print(bool(re.fullmatch(r"\d+", "123")))      # True
print(bool(re.fullmatch(r"\d+", "123abc")))   # False

"123"は全体が数字なので一致します。"123abc"は先頭は数字ですが末尾にabcが残るため一致しません。入力が「数字だけ」「メールアドレスの形式に完全に合う」といった形式全体の検証に使います。

全ての一致箇所をリストで受け取る(re.findall

re.findallは、パターンに一致する全ての部分をリストで返します。戻り値の形は、パターンのキャプチャグループ数によって変わります。

print(re.findall(r"\d+", "a12 b345 c6"))          # ['12', '345', '6']
print(re.findall(r"(\d+)", "a12 b345 c6"))        # ['12', '345', '6']
print(re.findall(r"([a-z])(\d+)", "a12 b345"))    # [('a', '12'), ('b', '345')]

キャプチャグループがない場合はマッチ全体のリスト、1個ならそのグループの文字列のリスト、2個以上ならタプルのリストになります。

Matchを1件ずつ受け取る(re.finditer

re.finditerは、パターンに一致する全ての部分を、Matchオブジェクトのイテレータとして返しますre.findallと違って、位置情報(.span())などMatchの情報を1件ずつ扱えます。

for m in re.finditer(r"\d+", "a12 b345"):
    print(m.group(), m.span())
# 12 (1, 3)
# 345 (5, 8)

一致した文字列だけでなく、どこにあるか・どのグループに何が入っているかも取り出したいときは、re.findallではなくre.finditerを使います。

置換・分割

書き換え行う関数を順に見ていきます。

置換する(re.sub

re.subは、パターンに一致する部分を別の文字列に置き換えます。

print(re.sub(r"\d+", "#", "a12 b345"))            # 'a# b#'
print(re.sub(r"\d+", "#", "a12 b345", count=1))   # 'a# b345'

\d+にマッチする部分を#に置き換えます。count引数を指定すると、置換する回数を制限できます。

置換文字列の中では、キャプチャグループを参照できます。\1\2のように番号で、名前付きグループなら\g<名前>で参照します(置換文字列もr"..."で書くのが安全です)。

print(re.sub(r"(\w+)@(\w+)", r"\2@\1", "user@example"))              # 'example@user'
print(re.sub(r"(?P<y>\d{4})-(?P<m>\d{2})", r"\g<m>/\g<y>", "2026-07"))   # '07/2026'

1つ目は、@の左右を入れ替えています。2つ目は、2026-07(年-月)を07/2026(月/年)に並べ替えています。\g<0>と書くとマッチ全体を参照できます。

置換の内容を動的に決めたいときは、置換文字列の代わりに関数を渡せます。関数はMatchを受け取り、置換後の文字列を返します。

print(re.sub(r"\d+", lambda m: str(int(m.group()) * 2), "a12 b345"))   # 'a24 b690'

一致した数字を2倍にしています。数字ごとに違う値へ置き換えたいときに便利です。

置換に加えて回数を返す(re.subn

re.subnは、re.subと同じ置換をしたうえで、(置換後の文字列, 置換回数)のタプルを返します。置換した回数も同時に知りたい場面で使います。

print(re.subn(r"\d+", "#", "a12 b345"))   # ('a# b#', 2)

置換した文字列と、置換した回数(2)が返ります。

パターンで分割する(re.split

re.splitは、パターンに一致する位置で文字列を分割し、リストで返します。str.splitより柔軟な区切り方ができます。

print(re.split(r"\s+", "a  b   c"))   # ['a', 'b', 'c']

\s+(1個以上の空白)で分割し、空白の連続数に関係なく3要素になります。

パターンにキャプチャグループを入れると、区切りに使った部分も結果に含まれます。

print(re.split(r"(\s+)", "a  b"))   # ['a', '  ', 'b']

分割されたabに加えて、区切り自体の' '(空白2個)もリストに入ります。区切り文字も残したいときに使います。

maxsplit引数で、分割回数の上限を指定できます。

print(re.split(r"\s+", "a b c d", maxsplit=1))   # ['a', 'b c d']

1回だけ分割するので、先頭のaと残り全体b c dの2要素になります。

補助

正規表現のパターンをオブジェクトとして定義するなど、補助の役割をする関数を順に見ていきます。

パターンの定義

re.compileは、パターンをあらかじめコンパイルして、Patternオブジェクトとして返します。同じパターンを何度も使う場面で、毎回パターンを解釈し直す手間を省けます。

pattern = re.compile(r"\d+")
print(pattern.findall("a12 b345"))   # ['12', '345']
print(pattern.search("a12").group())   # '12'

Patternオブジェクトには、reモジュールの関数と同名のメソッド(searchmatchfindallsubなど)があり、パターンを引数に取らない点だけが違います。ループの中で何度も同じパターンを使うときや、パターンを1か所にまとめて管理したいときに使います。

記号を含む文字列をリテラルにマッチさせる

re.escapeは、文字列中の正規表現メタ文字(.*()?など)にバックスラッシュを付けて、そのままの文字としてマッチできる形に変換します。

s = "1.2.3"
print(re.escape(s))                       # '1\\.2\\.3'
print(bool(re.search(s, "1x2y3")))        # True   .が任意文字と解釈されて誤マッチ
print(bool(re.search(re.escape(s), "1x2y3")))       # False
print(bool(re.search(re.escape(s), "value: 1.2.3")))   # True

"1.2.3"をそのままパターンとして使うと、.が「任意の1文字」と解釈されるため、"1x2y3"にも一致してしまいます。re.escapeを通すと.\.になり、リテラルの.だけにマッチするようになります。ユーザー入力や変数など、メタ文字が含まれるかもしれない文字列をパターンに組み込むときに使います。

まとめ

reモジュールの主な関数を、用途別に整理しました。

一致の有無や位置だけ知りたいならsearchmatchfullmatch、全件を取るならfindallfinditer、書き換えならsubsubnsplit、と目的で選ぶと迷いにくくなります。

-プログラミング
-