概要
正規表現についてPythonで理解する。
まず、Pythonでの正規表現の使い方をまとめて、その後に正規表現について解説する。
最後に、よく使う正規表現のパターンをまとめる。
はじめに 正規表現について
正規表現(regular expression)とは文字列ないで文字の組み合わせを照合するために用いられるパターンのことです。
これだけだと全く意味がわからないと思うので、正規表現が使われるケースを考えて、正規表現の目的を考えてみましょう。
次のような文章があるとします。
A: Pythonって知ってる?
B: うん。しっているよ。pythonでしょ?
C: 俺も知っている。蛇のことだよね?
B: 違うよ。プログラミング言語だよ!
A: 二人ともちがうよ! コメディ番組だよ!!(注: 空飛ぶモンティパイソン)
この文章の中で「Python」や「知っている」や「違う」という言葉が出ていますが、書き方がそれぞれ異なっています。
ここから例えば「パイソン」という言葉を、その言葉のバラつきを考慮した上で、一気に取得したいとき、正規表現という書き方を用います。
また、同じ日付を表しているデータがあったとしても、あるところでは「2021/08/29」と書かれていて、べつのところでは「21/08/29」と書かれたり、「2021.8.29」と書かれているかもしれません。このようにデータに対してばらつきがあるにもかかわらず、あるパターンで一気に取得したいときに正規表現は使われます。
今回はまずPythonでの正規表現の使い方をまとめて、次に正規表現の書き方を解説します。最後に、よく使うパターンをまとめます。
Section 1 Pythonでの正規表現の使い方
Pythonで正規表現を扱うためにはre
モジュールを使います。これは標準ライブラリですので、単にimport re
とインポートするだけです。
正規表現のメソッド(正規表現を扱う関数)を使う方法は2種類あります。その違いは筆者にはわかりませんが、ここでは両方を記載します。
Section 1.1 finditer すべてのパターンマッチを取得する
finditer
は指定した正規表現にマッチするすべての単語を取得するための方法です。次のコードはその例です。
コード
import re sentence = 'These are cakes. This is a test! That is a pen! It is on this table!' pattern = r'[T|t]his|[T|t]hat' prog = re.compile(pattern) # find all matches print('------------- find -------------') print('------------- prog.finditer(sentence) -------------') matches = prog.finditer(sentence) for match in matches: print('match = {}'.format(match)) print('match.span() = {}'.format(match.span())) print('match.group() = {}'.format(match.group())) print('------------- re.finditer(pattern, sentence) -------------') matches = re.finditer(pattern, sentence) for match in matches: print('match = {}'.format(match)) print('match.span() = {}'.format(match.span())) print('match.group() = {}'.format(match.group()))
結果
------------- find ------------- ------------- prog.finditer(sentence) ------------- match = <re.Match object; span=(17, 21), match='This'> match.span() = (17, 21) match.group() = This match = <re.Match object; span=(33, 37), match='That'> match.span() = (33, 37) match.group() = That match = <re.Match object; span=(57, 61), match='this'> match.span() = (57, 61) match.group() = this ------------- re.finditer(pattern, sentence) ------------- match = <re.Match object; span=(17, 21), match='This'> match.span() = (17, 21) match.group() = This match = <re.Match object; span=(33, 37), match='That'> match.span() = (33, 37) match.group() = That match = <re.Match object; span=(57, 61), match='this'> match.span() = (57, 61) match.group() = this
以下、コードの解説します。
- 検索される文章は
These are cakes. This is a test! That is a pen! It is on this table!
です。
- 正規表現のパターンは
r'[T|t]his|[T|t]hat'
です。この意味は「ThisかThatを見つけろ。ただし、先頭のTは小文字tでもいい」です。
- パターンに
r'xxxxxx'
がついていますが、これは「正規表現のパターンを使っているよ」ということをPythonに明示するためです。というのも、正規表現のパターンの中には、Pythonのエスケープシークエンスと同じ記号があるからです。例えば、\b
は正規表現としての意味は単語境界(Word Boundary)を意味しますが、エスケープシークエンスとしての意味はASCIIのバックスペース (BS)を意味します。ですので、このr'xxxxxx'
は「この文字は正規表現のパターンとして認識してね」ということをPythonに知らせています。基本的には正規表現のパターンにはつけたほうがいいと思います。
finditer
はコンパイルするものとそうでないものがあります。結果は同じです。
match
はマッチしているかということを表しています。もし、マッチしていなかったら、match = None
となります。
match.span()
はマッチしている単語がどこにあるかを示しています(0番目基準)。
match.group()
でマッチしている単語を取得します。
- 注意: 同じようにパターンマッチする単語を全てリストとして取得する関数に
findall
がありますが、あまりお勧めしません(ですので省略します)。
Section 1.2 sub 正規表現にマッチする単語を指定の文字に入れ替える
sub
は指定した正規表現にマッチする単語を指定の文字に入れ替えるための方法です。次のコードはその例です。
コード
import re sentence = 'These are cakes. This is a test! That is a pen! It is on this table!' pattern = r'[T|t]his|[T|t]hat' prog = re.compile(pattern) # replace print('------------- replace -------------') print('------------- prog.sub(repl, sentence) -------------') repl = 'TTTT' repl_sentence = prog.sub(repl, sentence) print('repl_sentence = {}'.format(repl_sentence)) print('------------- re.sub(pattern, repl, sentence) -------------') repl_sentence = re.sub(pattern, repl, sentence) print('repl_sentence = {}'.format(repl_sentence))
結果
------------- replace ------------- ------------- prog.sub(repl, sentence) ------------- repl_sentence = These are cakes. TTTT is a test! TTTT is a pen! It is on TTTT table! ------------- re.sub(pattern, repl, sentence) ------------- repl_sentence = These are cakes. TTTT is a test! TTTT is a pen! It is on TTTT table!
sub
は入れ替えですが、主にパターンマッチする単語をなくすときに使われると思います。つまりこの例でいうと「This、That、this、thatをなくした文章が欲しい」というとき、repl_sentence = re.sub(pattern, '', sentence)
と書けばいいです。
Section 1.3 split 正規表現にマッチする単語で文章を分ける
split
は指定した正規表現にマッチする単語で文章を分けてリストを返します。次のコードはその例です。
コード
import re sentence = 'These are cakes. This is a test! That is a pen! It is on this table!' pattern = r'[T|t]his|[T|t]hat' prog = re.compile(pattern) # split print('------------- split -------------') print('------------- prog.split(sentence) -------------') splited_sentences = prog.split(sentence) print('splited_sentences = {}'.format(splited_sentences)) for sp_sentence in splited_sentences: print('sp_sentence = {}'.format(sp_sentence)) print('------------- re.split(pattern, sentence) -------------') splited_sentences = re.split(pattern, sentence) print('splited_sentences = {}'.format(splited_sentences)) for sp_sentence in splited_sentences: print('sp_sentence = {}'.format(sp_sentence))
結果
------------- split ------------- ------------- prog.split(sentence) ------------- splited_sentences = ['These are cakes. ', ' is a test! ', ' is a pen! It is on ', ' table!'] sp_sentence = These are cakes. sp_sentence = is a test! sp_sentence = is a pen! It is on sp_sentence = table! ------------- re.split(pattern, sentence) ------------- splited_sentences = ['These are cakes. ', ' is a test! ', ' is a pen! It is on ', ' table!'] sp_sentence = These are cakes. sp_sentence = is a test! sp_sentence = is a pen! It is on sp_sentence = table!
Section 1.4 match 文章の先頭が正規表現にマッチするかどうか
match
は指定した文章の先頭が正規表現にマッチするかどうかを調べます。
コード
import re pattern = r'[T|t]his|[T|t]hat' prog = re.compile(pattern) # match print('------------- match -------------') #### true case sentence = 'This is a test! That is a pen! It is on this table!' match = prog.match(sentence) # It can be written instead re.match(pattern, sentence) print('match = {}'.format(match)) if match: print('match.group() = {}'.format(match.group())) #### false case sentence = 'These are cakes. This is a test! That is a pen! It is on this table!' match = prog.match(sentence) # It can be written instead re.match(pattern, sentence) print('match = {}'.format(match)) if match: print('match.group() = {}'.format(match.group()))
結果
------------- match ------------- match = <re.Match object; span=(0, 4), match='This'> match.group() = This match = None
- 注意:
match
は文章の先頭から数えてパターンにマッチするかどうかということしか調べません。ですので、本来はあまり役に立つ機会はありません。パターンにマッチしていなかった場合、None
を返します。
Section 1.5 search 文章が正規表現にマッチするかどうか
search
は指定した文章が正規表現にマッチするかどうかを調べます。ただし、少なくとも1つマッチしたらそこで処理は終了です。
コード
import re pattern = r'[T|t]his|[T|t]hat' prog = re.compile(pattern) # search print('------------- search -------------') #### true case sentence = 'This is a test! That is a pen! It is on this table!' match = prog.search(sentence) # It can be written instead re.search(pattern, sentence) print('match = {}'.format(match)) if match: print('match.group() = {}'.format(match.group())) #### true case sentence = 'These are cakes. This is a test! That is a pen! It is on this table!' match = prog.search(sentence) # It can be written instead re.search(pattern, sentence) print('match = {}'.format(match)) if match: print('match.group() = {}'.format(match.group()))
結果
------------- search ------------- match = <re.Match object; span=(0, 4), match='This'> match.group() = This match = <re.Match object; span=(17, 21), match='This'> match.group() = This
- 注意:
search
は先ほどのmatch
とは違い、文章にパターンがマッチするかどうかを調べます。ですので、今回は両方ともパターンがマッチしました。パターンにマッチしていなかった場合、None
を返します。
search
やmatch
はあまり使う機会がないかもしれませんが、「この文章にこれこれのパターンが入っているならば、しかじかの処理をおこなう」などのように「パターンが入っているかどうか」のみを確認するときに使える関数だと思います。
Section 1.6 IGNORECASE 大文字・小文字の区別なしのためのフラグ
これまで「This/this、That/that」をパターンとして認識するために、r'[T|t]his|[T|t]hat'
と書きましたが、大文字と小文字の区別をなくして検索するフラグ IGNORECASE
を使えば、もう少しすっきりと書くことができます。以下のコードはその例です。
コード
import re sentence = 'These are cakes. This is a test! That is a pen! It is on this table!' pattern = r'this|that' prog = re.compile(pattern, re.IGNORECASE) # instead it can be written by re.compile(pattern, flags=re.IGNORECASE) print('------------- Ignore Flag -------------') print('------------- prog.finditer(sentence) -------------') matches = prog.finditer(sentence) for match in matches: print('match = {}'.format(match)) print('match.group() = {}'.format(match.group())) print('------------- re.finditer(pattern, sentence, re.IGNORECASE) -------------') matches = re.finditer(pattern, sentence, re.IGNORECASE) # instead it can be written by re.finditer(pattern, sentence, flags=re.IGNORECASE) for match in matches: print('match = {}'.format(match)) print('match.group() = {}'.format(match.group()))
結果
------------- Ignore Flag ------------- ------------- prog.finditer(sentence) ------------- match = <re.Match object; span=(17, 21), match='This'> match.group() = This match = <re.Match object; span=(33, 37), match='That'> match.group() = That match = <re.Match object; span=(57, 61), match='this'> match.group() = this ------------- re.finditer(pattern, sentence, re.IGNORECASE) ------------- match = <re.Match object; span=(17, 21), match='This'> match.group() = This match = <re.Match object; span=(33, 37), match='That'> match.group() = That match = <re.Match object; span=(57, 61), match='this'> match.group() = this