プログラミング

正規表現のフラグ(Python)

正規表現のフラグは、マッチの挙動を変えるオプションです。たとえば「大文字小文字を区別しない」「^$を行ごとに効かせる」「.を改行にもマッチさせる」といった切り替えができます。

たとえば「Cat CAT cat」からcatを取り出す場合、ふつうは大文字小文字が区別され、小文字のcatしか取れません。ここに「大文字小文字を無視する」フラグre.IGNORECASEを渡すと、CatCATcatのすべてが取れます。フラグは、re.findallなどの関数に引数として渡して使います。

本記事では、よく使う5つのフラグと、パターンの中に直接書くインラインフラグを、Pythonのreモジュールで一つずつ整理します。reモジュールは標準モジュールなのでインストールは不要です。

なお、本記事で使う^$.\d\w\sなどの基本構文や(?:...)など?で始まる丸かっこの記法(グループ)は過去に記事で扱っています。

動作確認

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

目次

フラグの一覧

主要なフラグを、先に一覧で示します。短縮形(re.Iなど)は正式名と同じ意味で、どちらを使っても同じように動作します。

フラグ短縮形はたらき
re.IGNORECASEre.I大文字小文字を区別せずにマッチする
re.MULTILINEre.M^$を各行の先頭・末尾にもマッチさせる
re.DOTALLre.S.を改行文字にもマッチさせる
re.VERBOSEre.Xパターン中の空白とコメントを無視し、読みやすく書ける
re.ASCIIre.A\d\w\sなどをASCII文字だけに限定する

フラグは関数のflags引数に渡します(re.findall(パターン, 文字列, re.I))。複数を同時に使うときは|でつなぎます。また、パターンの中に直接書くインライン形((?i)(?i:...))もあります。

大文字小文字を無視する(re.IGNORECASE)

re.IGNORECASE(短縮形re.I)を渡すと、大文字小文字を区別せずにマッチします。

print(re.findall(r"cat", "Cat CAT cat"))                  # ['cat']
print(re.findall(r"cat", "Cat CAT cat", re.IGNORECASE))   # ['Cat', 'CAT', 'cat']

フラグなしでは小文字のcatしか一致しませんが、re.IGNORECASEを付けるとCatCATcatのすべてが一致します。

re.IGNORECASEはASCIIだけでなく、Unicodeの大文字小文字も対象です(Ääなど)。

print(re.findall(r"ä", "Ä ä", re.IGNORECASE))   # ['Ä', 'ä']

ASCIIの範囲(azAZ)だけにしたい場合は、後述のre.ASCIIを併用します。

print(re.findall(r"ä", "Ä ä", re.IGNORECASE | re.ASCII))   # ['ä']

行ごとに先頭・末尾を判定する(re.MULTILINE)

^(先頭)と$(末尾)は、ふつうは文字列全体の先頭・末尾だけにマッチします。re.MULTILINE(短縮形re.M)を渡すと、改行で区切られた各行の先頭・末尾にもマッチするようになります。

text = "apple pie\nbanana bread\ncherry cake"
print(re.findall(r"^\w+", text))                 # ['apple']
print(re.findall(r"^\w+", text, re.MULTILINE))   # ['apple', 'banana', 'cherry']

^\w+は「行頭の単語」を表します。フラグなしでは、^が文字列全体の先頭にしかマッチしないため、1行目のappleだけが取れます。re.MULTILINEを付けると、^が各行の先頭にマッチし、各行の先頭の単語applebananacherryが取れます。

末尾の$も同じです。同じtextに対して、行末の単語を表す\w+$で見てみます。

print(re.findall(r"\w+$", text))                 # ['cake']
print(re.findall(r"\w+$", text, re.MULTILINE))   # ['pie', 'bread', 'cake']

フラグなしでは、$が文字列全体の末尾にしかマッチしないため、最終行のcakeだけが取れます。re.MULTILINEを付けると、$が各行の末尾にマッチし、各行の末尾の単語piebreadcakeが取れます。

補足:re.MULTILINEを付けてもre.matchは先頭のみ

ここで注意が必要なのがre.matchです。re.matchは、re.MULTILINEを付けても、文字列の先頭でしか照合しません。

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

re.matchは「文字列の先頭から一致するか」を見る関数で、これはフラグでは変わりません。各行の先頭で探したいときは、^re.MULTILINEを組み合わせてre.findallre.searchを使います。

ドットを改行にもマッチさせる(re.DOTALL)

.は、ふつうは改行文字(\n)以外の任意の1文字にマッチします。re.DOTALL(短縮形re.S)を渡すと、.が改行にもマッチするようになります。

text = "start\nend"
print(re.findall(r"start.end", text))              # []
print(re.findall(r"start.end", text, re.DOTALL))   # ['start\nend']

フラグなしでは.が改行にマッチしないため、結果は空です。re.DOTALLを付けると改行にもマッチし、start\nend全体が取れます。改行をまたいでまとめてマッチさせたいときに使います。

パターンに空白やコメントを書く(re.VERBOSE)

re.VERBOSE(短縮形re.X)を渡すと、パターン中の空白と、#から行末までのコメントが無視されます。複雑なパターンを、改行・字下げ・コメント付きで読みやすく書けます。

pattern = r"""
    \d{3}    # 市外局番
    -        # ハイフン
    \d{4}    # 番号
"""
print(re.findall(pattern, "012-3456 と 78-9012", re.VERBOSE))   # ['012-3456']

このパターンは、空白とコメントを取り除くと\d{3}-\d{4}と同じです。012-3456はこの形に合うので取れ、78-9012は先頭が2桁なので取れません。

注意点として、re.VERBOSEでは空白そのものが無視されます。空白を文字としてマッチさせたいときは、\ (バックスラッシュ+空白)や[ ](空白だけの文字クラス)と書きます。

ASCIIだけにマッチさせる(re.ASCII)

\d\w\sは、Python3では既定でUnicode全体(日本語や全角文字なども含む)にマッチします。re.ASCII(短縮形re.A)を渡すと、これらをASCII文字だけに限定できます。

print(re.findall(r"\w+", "今はPython3"))            # ['今はPython3']
print(re.findall(r"\w+", "今はPython3", re.ASCII))   # ['Python3']

フラグなしでは、\w今はも語の文字とみなすため、今はPython3全体が1つのまとまりとして取れます。re.ASCIIを付けると、\wは英数字とアンダースコア(_)だけになり、今はは除外されてPython3だけが取れます。半角の英数字だけを対象にしたいときに使います。

複数のフラグを同時に使う

複数のフラグを同時に使うときは、|(縦棒)でつなぎます。

print(re.findall(r"^apple", "Apple\nAPPLE", re.IGNORECASE | re.MULTILINE))   # ['Apple', 'APPLE']

re.IGNORECASE | re.MULTILINEで、「大文字小文字を区別せず」かつ「各行の先頭で」マッチします。Apple(1行目)とAPPLE(2行目)の両方が取れます。3つ以上でもre.I | re.M | re.Sのようにつなげます。

パターンに直接書く(インラインフラグ)

フラグは、flags引数で渡すほかに、パターンの中に直接書くこともできます。これをインラインフラグと呼びます。書き方は次の3つです。

書き方効果
(?i)パターン全体に効かせる(先頭に置く)
(?i:…)...の部分だけに効かせる
(?-i:…)...の部分だけフラグを外す

表のiは、re.IGNORECASEの短縮形re.Iを小文字にしたものです。同じくmre.MMULTILINE)、sre.SDOTALL)、xre.XVERBOSE)、are.AASCII)に対応し、iの位置をこれらに置き換えれば同じように使えます。

パターン全体に効かせるには、先頭に(?i)のように書きます。

print(re.findall(r"(?i)cat", "Cat CAT cat"))   # ['Cat', 'CAT', 'cat']

(?i)re.IGNORECASEを渡すのと同じです。なお、パターン全体に効かせる(?i)は、パターンの先頭に置く必要があります。

パターンの一部にだけ効かせたいときは、(?i:...)という形で、対象を丸かっこで囲みます。

print(re.findall(r"(?i:cat)dog", "CATdog catDOG Catdog"))   # ['CATdog', 'Catdog']

(?i:cat)の部分だけが大文字小文字を無視するので、catCATCatでも一致しますが、続くdogは区別されます。catDOGDOGdogと一致しないため、取れるのはCATdogCatdogです。

逆に、(?-i:...)と書くと、その部分だけフラグを外せます。

print(re.findall(r"(?i)(?-i:CAT)dog", "CATdog catdog CATDOG"))   # ['CATdog', 'CATDOG']

ここでは(?i)で全体を大文字小文字無視にしつつ、(?-i:CAT)の部分だけ区別を戻しています。CATは大文字でなければならず、dogは区別されないので、CATdogCATDOGが取れます(catdogCATの部分が小文字なので取れません)。

まとめ

正規表現のフラグについて、reモジュールで例を示しながら確認しました。

  • re.IGNORECASEre.I)は大文字小文字を区別せずにマッチする。
  • re.MULTILINEre.M)は^$を各行の先頭・末尾にもマッチさせる。re.matchは付けても先頭のみ。
  • re.DOTALLre.S)は.を改行にもマッチさせる。
  • re.VERBOSEre.X)はパターン中の空白とコメントを無視し、読みやすく書ける。
  • re.ASCIIre.A)は\d\w\sなどをASCII文字だけに限定する。

複数のフラグは|でつなぎ、パターンに直接書くインライン形(全体は(?i)、一部は(?i:...)(?-i:...))も使えます。

なお、次の記事「正規表現のreモジュールの関数(Python)」で正規表現を応用した検索・置換などの関数を取り扱います。

-プログラミング
-