正規表現を使うと、かなり複雑な条件での文字列検索をおこなうことができる。

Pythonで正規表現(regex)を使う場合、組み込みのreモジュールを使用する。

今回はその使い方をいくつか例を挙げて紹介しよう。

正規表現を使って文字列を分割する

reモジュールの関数は、パターンによるマッチング、分割、置換と、3つに分かれた機能を持っている。

例として、複数の空白やタブ文字が入った文字列変数txtがあるとしよう。

余分な空白文字などを除外した文字列のみを取り出し、文字列ごとに分割したい場合、以下のようにsplit関数を使う。

import re

txt = "foo    bar\t hoge    \tfuga"

re.split('\s+', txt)
# ['foo', 'bar', 'hoge', 'fuga']

split関数の第一引数には正規表現のパターン「\s+」を指定している。
「\s」は空白文字(改行文字・タブ)を表し、「+」は直前の1文字の1回以上の繰り返しを表すので、マッチしたパターンが区切り文字となり、結果として単語ごとに分割されたリストを得ることができる。

正規表現を使って指定条件のマッチングをおこなう

マッチした文字列全てをリストとして取得する

分割の例では、マッチした文字列を区切り文字としたが、マッチした文字列自体を取得したい場合、findall関数を使う。

import re

txt = "foo    bar\t hoge    \tfuga"

re.findall('\s+', txt)
# ['    ', '\t ', '    \t']

正規表現パターンをオブジェクトとして定義する

同じ正規表現を、複数の文字列に対して適用する場合、re.compileでパターンをオブジェクトとして定義した方が良い。
処理のパフォーマンスが向上する。

regex = re.compile('\s+')
regex.findall(txt)
# ['    ', '\t ', '    \t']

search関数とmatch関数

findall関数はマッチした全ての文字列を取得したが、search関数は最初にマッチした文字列のみを取得する。

また、match関数は更に厳格な関数で、文字列の先頭のみでマッチするかのみを調べる。

それぞれの関数の違いは、実例を見た方が分かりやすいと思うので以下のコードを確認してくれ。

import re

mail_list = """hoge@gmail.com
fuga@gmail.com
foo@gmail.com
bar@yahoo.com
"""

pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'

# 正規表現で大文字・小文字を区別しないようにする
regex = re.compile(pattern, flags=re.IGNORECASE)


### findall関数
regex.findall(mail_list)
# ['hoge@gmail.com', 'fuga@gmail.com', 'foo@gmail.com', 'bar@yahoo.com']


### search関数
m = regex.search(mail_list)
# <re.Match object; span=(0, 14), match='hoge@gmail.com'>

mail_list[m.start():m.end()]
# 'hoge@gmail.com'


### match関数
print(regex.match(mail_list))
# <re.Match object; span=(0, 14), match='hoge@gmail.com'>

# 文字列の先頭が一致しない場合はNoneが返される
mail_list = """@@@hoge@gmail.com
fuga@gmail.com
foo@gmail.com
bar@yahoo.com
"""
print(regex.match(mail_list))
# None

正規表現を使って文字列を置換する

最後に文字列置換の例を見ていこう。

置換の場合はsub関数を使う。

先述のマッチングの例で使ったメールリストをそのまま使って例を見ていこう。

import re

mail_list = """hoge@gmail.com
fuga@gmail.com
foo@gmail.com
bar@yahoo.com
"""

pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'

# 正規表現で大文字・小文字を区別しないようにする
regex = re.compile(pattern, flags=re.IGNORECASE)

print(regex.sub('mail_address', mail_list))
# mail_address
# mail_address
# mail_address
# mail_address

正規表現でメールアドレスを細かく分割する

少し正規表現自体の話にシフトするが、ここではメールアドレスをユーザー名、ドメイン名、トップレベルドメインに分割する例を紹介しておく。

import re

mail_list = """hoge@gmail.com
fuga@gmail.com
foo@gmail.com
bar@yahoo.com
"""

pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'

# 正規表現で大文字・小文字を区別しないようにする
regex = re.compile(pattern, flags=re.IGNORECASE)

m1 = regex.match('hoge@gmail.com')
m1.groups()
# ('hoge', 'gmail', 'com')

m2 = regex.findall(mail_list)
# [('hoge', 'gmail', 'com'),
#  ('fuga', 'gmail', 'com'),
#  ('foo', 'gmail', 'com'),
#  ('bar', 'yahoo', 'com')]