Pythonによる正規表現入門

このチュートリアルでは、Pythonで正規表現を使うことについて、その構文やPythonの組み込みモジュールを使って構築する方法などを学びます。

そのために、Pythonのreモジュールのさまざまな操作と、Pythonアプリケーションでの使用方法について説明します。

正規表現とは何ですか?

正規表現は基本的に、テキストを検索するための検索パターンを定義するために使用できる文字のシーケンスに過ぎません。

この「検索エンジン」はPythonプログラミング言語(他の多くの言語も同様)に組み込まれており、 re モジュールを通じて利用することができます。

正規表現(略して「regex」)を使うには、通常、マッチさせたい文字列のセットのルールを指定し、「この文字列はパターンにマッチするか」、あるいは「この文字列のどこかにパターンにマッチするものがあるか」といった質問を自分に投げかけます。

また、正規表現を使って文字列を修正したり、さまざまな方法で文字列を分割したりすることもできます。

これらの「高次」操作は、すべて最初にテキストと正規表現文字列とのマッチングから始まり、マッチングが見つかった時点で文字列を操作することができます (分割など)。

これらはすべて、Pythonの re モジュールによって実現されています。

このモジュールについては、後のいくつかのセクションでさらに詳しく見ていきます。

正規表現の構文

正規表現は、入力文字列にマッチすることを目的としたパターンを指定します。

このセクションでは、文字列のマッチングに使用できる特殊文字とパターンをいくつか紹介します。

一致する文字

正規表現には特殊文字と普通の文字の両方を含めることができます。

A’, ‘a’, ‘0’ のような普通の文字は、最も単純な正規表現で、単にそれ自身にマッチします。

例えば、^, $, *, +, ?, {, }, [, ], |, (, and ) などの特殊文字は、それ自身にマッチすることはありません。

これは、この表でさらに説明する高次のマッチング機能に使用されるからである。

メタキャラクタ 説明
* 直前の要素に0回以上マッチします。例えば、 ab*c は “ac”, “abc”, “abbbc”, などにマッチします。xyz]*は "", "x", "y", "z", "zx", "zyx", "xyzzy", などとマッチします。(ab)* は “”, “ab”, “abab”, “ababab “などにマッチします。
+|直前の要素に1回以上マッチします。例えば、ab+cは “abc”, “abbc”, “abbbc “などにマッチするが、”ac “にはマッチしない。
?|直前の要素に0回または1回だけマッチします。例えば、ab?c は “ac” または “abc” のみにマッチする。
| 選択演算子(交代演算子または集合和演算子としても知られています)は、この演算子の前の式または後の式のいずれかにマッチします。例えば、abc|def は “abc” と “def” のどちらにもマッチします。
多くのアプリケーションでは改行文字は除外されています。POSIXの括弧式の中では、ドット文字はドットそのものにマッチします。例えば、a.c は “abc” などにマッチしますが、 [a.c] は “a”, “.”, または “c” だけにマッチします。
^ startsWith() 関数のように、文字列の開始位置にマッチします。ラインベースツールでは、任意の行の開始位置にマッチする。
?|文字列の終了位置、または endsWith() 関数のように文字列の終了改行の直前の位置にマッチします。ラインベースツールでは、任意の行の終端位置にマッチします。

正規表現の説明の一部はWikipediaの記載を引用しています。

Python における正規表現のメソッド

正規表現を利用するために、いくつかのメソッドがあります。

ここでは、最も一般的に使用されるメソッドについて説明し、また、それらがどのように使用されるかのいくつかの例を示します。

これらのメソッドは以下の通りです。

  1. re.match()
  2. re.search()メソッド
  3. re.findall()
  4. re.split()
  5. re.sub()
  6. re.compile()。

re.match(pattern, string, flags=0)

この式は、文字列の先頭の文字または文字の集合にマッチさせるために使用されます。

また、この式は文字列の先頭のみにマッチし、与えられた文字列が複数行ある場合は各行の先頭にはマッチしないことに注意することが重要です。

Pythonは文字列の先頭には現れないので、以下の式は None を返します。

# match.py


import re
result = re.match(r'Python', 'It's  easy to learn Python. Python also has elegant syntax')


print(result)


$ python match.py
None


re.search(pattern, string)

このモジュールは、与えられた文字列のどこかにマッチするものがあるかどうかを調べ、見つかった場合はその結果を返し、見つからなかった場合は None を返します。

次のコードでは、”Daisy found a puppy” という文字列の中に “puppy” という単語が含まれているかどうかを調べています。

# search.py


import re


if re.search("puppy", "Daisy found a puppy."):
    print("Puppy found")
else:
    print("No puppy")


ここでは、まず re モジュールをインポートして、それを使って “Daisy found a puppy” という文字列の中に “puppy” という部分文字列があるかどうかを検索しています。

もし文字列中に存在すれば、re.MatchObject が返され、if 文で評価されたときに “truthy” と判断されます。

$ python search.py 
Puppy found


re.compile(pattern, flags=0)

このメソッドは、正規表現パターンを正規表現オブジェクトにコンパイルするために使用されます。

このオブジェクトは、上で説明した match()search() メソッドを用いてマッチングするために使用することができます。

正規表現文字列のパースや処理には計算コストがかかるため、このメソッドによって時間を節約することもできます。

# compile.py


import re


pattern = re.compile('Python')
result = pattern.findall('Pythonistas are programmers that use Python, which is an easy-to-learn and powerful language.')


print(result)


find = pattern.findall('Python is easy to learn')


print(find)


$ python compile.py 
['Python', 'Python']
['Python']


Pythonistas” の場合は単語全体が返されるのに対して、マッチした文字列だけが返されることに注目しましょう。

これは、特殊なマッチ文字が含まれる正規表現文字列を使用する場合に便利です。

re.sub(pattern, repl, string)

この式はその名の通り、パターンが出現した場合に新しい文字列を検索して代入するために使われる。

# sub.py


import re
result = re.sub(r'python', 'ruby', 'python is a very easy language')


print(result)


$ python sub.py 
ruby is a very easy language


re.findall(pattern, string)

このセクションの前に見てきたように、このメソッドは与えられた文字列の中にあるすべての出現箇所のリストを検索して取得します。

これは、 re.search()re.match() の関数とプロパティの両方を兼ね備えています。

次の例では、文字列から “Python” が出現する箇所をすべて取得します。

# findall.py


import re


result = re.findall(r'Python', 'Python is an easy to learn, powerful programming language. Python also has elegant syntax')
print(result)


$ python findall.py 
['Python', 'Python']


繰り返しますが、このような完全一致文字列(“Python”)を使うのは、与えられた文字列の中に正規表現文字列があるかどうか、あるいはそれが何回現れるかを見つけるためにのみ有効です。

re.split(pattern, string, maxsplit=0, flags=0)

この式は、指定されたパターンが文字列内で発生する位置で文字列を分割します。

また、パターンに括弧を捕捉するような高度な機能が使用されている場合、パターン内のすべてのグループのテキストを返します。

# split.py


import re


result =  re.split(r"y", "Daisy found a puppy")


if result:
    print(result)
else:
   print("No puppy")


上で見たように、文字パターン「y」は3回出現し、式はそれが出現するすべてのインスタンスで分割されています。

$ python split.py 
['Dais', ' found a pupp', '']


正規表現の実用的な使い方

知ってか知らずか、私たちはアプリケーションの中でほとんど毎日正規表現を使っています。

正規表現はほぼすべてのプログラミング言語で利用できるため、その利用法から逃れることは容易ではありません。

ここでは、アプリケーションでの正規表現の使い方のいくつかを見てみましょう。

URLの構築

全ての Web ページには URL があります。

ここで、”http://www.example.com/products/27/” のようなアドレスを持つ Django ウェブサイトがあるとします。

ここで、27 は製品の ID です。

27 は製品の ID です。

全ての製品にマッチするように別々のビューを書くのは非常に面倒です。

しかし、正規表現を使えば、URL にマッチして ID を抽出してくれるパターンを作成することができます。

任意の数値のIDにマッチして抽出する式は ^products/(d+)/$ となります。

  • ^products/ は Django に、URL の先頭に “products/” がある文字列にマッチするよう指示します (ここで文字列の “先頭” は ^ で指定されます)。
  • (d+) は、数字 (d+ で指定) があり、それを捕捉して抽出する (パレンテースで指定) ことを意味します。
  • / は Django に、もう一つの “/” 文字が続くように指示します。
  • つまり、/ で終わる文字列だけがこのパターンにマッチします。

電話番号の検証

次の例は、接頭辞付きのカナダ番号のリストを検証するために使用されます。

# validate_email.py


import re


email = "example@gmail.com"


if not re.match(re.compile(r'^.+@[^.].*.[a-z]{2,10}$', flags=re.IGNORECASE), email):
    print("Enter a valid email address")
else:
    print("Email address is valid")


$ python validate_email.py 
Email address is valid


見ての通り、2番目の番号は “+”ではなく”=”を使っているため、無効と判断される。

不要なコンテンツのフィルタリング

正規表現を使用すると、投稿コメントから特定の単語を除外することもできます。

これは、ブログ記事やソーシャルメディアにおいて特に便利です。

次の例では、ユーザーがコメントで使用すべきでない単語を事前に選択してフィルタリングする方法を示しています。

# validate_numbers.py


import re


numbers = ["+18009592809", "=18009592809"]


for number in numbers:
    if not re.match(re.compile(r"^(+1?[-. ]?(d+))$"), number):
        print("Number is not valid")
    else:
        print("Number is valid")


$ python validate_numbers.py 
Number is valid
Number is not valid


結論

このチュートリアルでは、あらゆるアプリケーションで正規表現を使えるようになるために必要なことを説明しました。

re モジュールのドキュメントには、あなたのアプリケーションの目標を達成するのに役立つ多くのリソースがあるので、自由に参照してください。

タイトルとURLをコピーしました