正規表現、略して RegEx は、テキストの検索や置換、検証、文字列分割などに使用できるパターンの表現です。
これらのパターンは、文字、数字、特殊文字で構成され、検索するテキストの特定のセグメントにマッチするような形式になっています。
正規表現はパターンマッチングに広く使われており、様々なプログラミング言語が正規表現を表現するためのインターフェースや、マッチの結果を扱うためのインターフェースを持っています。
今回は、Pythonでメールアドレスを検証する方法について、正規表現を用いて説明します。
>
Pythonの正規表現のインターフェースについて詳しく知りたい方は、「Pythonの正規表現ガイド」をご覧ください! > Pythonの正規表現のインターフェースについて詳しく知りたい方は、「Pythonの正規表現ガイド」をご覧ください。
汎用電子メール正規表現
有効な電子メール・アドレスのすべてに一致する正規表現は存在しないことに留意してください。
しかし、ほとんどの有効な電子メールアドレスにマッチする表現は存在します。
どのような電子メール アドレスの形式を探すのかを定義する必要があります。
最も一般的な電子メールの形式は次のとおりです。
(username)@(domainname).(top-leveldomain)
したがって、@
という記号がドメインセグメントとプレフィックスを分けているパターンに集約されます。
プレフィックスは受信者の名前です。
大文字と小文字、数字、そして.(ドット)、
–(ハイフン)、
_`(アンダースコア)といった特殊文字を含む文字列です。
ドメインは、名前とトップレベルドメインを .
(ドット) 記号で分割したものからなります。
ドメイン名には、大文字、小文字、数字、-
(ハイフン)記号を使用することができます。
さらに、トップレベルドメイン名は少なくとも2文字(すべて大文字か小文字)でなければなりませんが、これより長くすることもできます。
注:有効なメールについては、文字数や使用できる特定の文字など、もっと細かいルールがたくさんあります。
ここでは、汎用的なアプローチの後に、RFC5322で定義されている拡張された、非常に失敗の少ない正規表現について見ていきます。
簡単に言うと、私たちのメールの正規表現は次のようになります。
(string1)@(string2).(2+characters)
これは、次のような電子メール・アドレスに正しくマッチします。
name.surname@gmail.com
anonymous123@yahoo.co.uk
my_email@outlook.co
再度、同じ表現を使用すると、これらの電子メール・アドレスは失敗します。
johnsnow@gmail
anonymous123@...uk
myemail@outlook.
注意すべきは、文字列に特定の特殊文字を含んではいけないということです。
さらに、トップレベルドメインは ..
であってはなりません。
これらのケースも考慮して、これらの規則を具体的な表現に落とし込むと、最初の表現よりもいくつかのケースを考慮したものになります。
([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(.[A-Z|a-z]{2,})+
接頭辞の特殊文字は @
記号の直前や、記号で始まることはできないので、特殊文字の前後には少なくとも1文字の英数字があるようにしました。
ドメインについては、メールにはドットで区切られたいくつかのトップレベルドメインが含まれることがあります。
明らかに、この正規表現は最初のものよりも複雑ですが、電子メールのフォーマットについて定義したすべてのルールをカバーしています。
しかし、この正規表現もまた、私たちが思いもよらないようなエッジケースを適切に検証できない可能性があります。
Pythonでメールアドレスの検証をする
reモジュールは Python で正規表現を表現し、扱うためのクラスとメソッドを含んでいるので、それをスクリプトにインポートします。
今回使用するメソッドはre.fullmatch(pattern, string, flags)です。
このメソッドは、文字列全体がパターンにマッチする場合のみマッチオブジェクトを返し、それ以外の場合はNone` を返します。
注意: re.fullmatch()
は Python 3.4 で導入されましたが、それ以前は re.match()
が代わりに使われていました。
新しいバージョンでは、fullmatch()
が優先されます。
先ほどの正規表現を compile()
して、メールアドレスを受け取り、それを検証する簡単な関数を定義してみましょう。
import re
regex = re.compile(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(.[A-Z|a-z]{2,})+')
def isValid(email):
if re.fullmatch(regex, email):
print("Valid email")
else:
print("Invalid email")
re.compile()` メソッドは正規表現パターンをregexオブジェクトにコンパイルします。
これは主に効率的な理由から、パターンを複数回マッチさせる場合に使用されます。
では、先ほど見てきたいくつかの例でコードをテストしてみましょう。
isValid("name.surname@gmail.com")
isValid("anonymous123@yahoo.co.uk")
isValid("anonymous123@...uk")
isValid("...@domain.us")
この結果は
Valid email
Valid email
Invalid email
Invalid email
素晴らしい!これでシステムが機能するようになりました。
ロバストなメール正規表現
上記で使用した式は、ほとんどの場合においてうまく機能し、合理的なアプリケーションであればうまく機能します。
しかし、セキュリティに関心が高い場合や、正規表現を書くのが好きな場合は、有効な電子メールアドレスを通過させながら、可能性の範囲を狭めることを選択することができます。
長い表現は少し複雑になり、読みにくくなる傾向がありますが、この表現も例外ではありません。
(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=^_`{|}~-]+)*
|"(?:[x01-x08x0bx0cx0e-x1fx21x23-x5bx5d-x7f]
|[x01-x09x0bx0cx0e-x7f])*")
@
(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
|[(?:(?:(2(5[0-5]|[0-4][0-9])
|1[0-9][0-9]|[1-9]?[0-9])).){3}(?:(2(5[0-5]|[0-4][0-9])
|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[x01-x08x0bx0cx0e-x1fx21-x5ax53-x7f]
|[x01-x09x0bx0cx0e-x7f])+)])
- bortzmeyerによるRegExの翻案
これは、入力されたメールアドレスの99.99%をカバーするRFC5322準拠の正規表現です。
♪ 言葉で説明するのは一般的にNGですが、視覚化することでかなり楽になります。
Ίταμμα
*୧⃛(๑⃙⃘⁼̴̀꒳⁼̴́๑⃙⃘)
実は、RFC5322を満たす表現はこれだけではありません。
程度の差はあれ、多くの表現がRFC5322を満たしています。
Pythonの re.compile()
メソッドにインポートして式を表現することで、仕様に準拠した短いバージョンを簡単に作成できます。
import re
regex = re.compile(r"([-!#-'*+/-9=?A-Z^-~]+(.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ ]|([ -~]))+")@([-!#-'*+/-9=?A-Z^-~]+(.[-!#-'*+/-9=?A-Z^-~]+)*|[[ -Z^-~]*])")
def isValid(email):
if re.fullmatch(regex, email):
print("Valid email")
else:
print("Invalid email")
isValid("name.surname@gmail.com")
isValid("anonymous123@yahoo.co.uk")
isValid("anonymous123@...uk")
isValid("...@domain.us")
これもまた、次のような結果になります。
Valid email
Valid email
Invalid email
Invalid email
結論
このガイドをまとめるために、学んだことを復習しておきましょう。
正規表現を使ってメールを検証する方法はたくさんありますが、そのほとんどは求める特定の形式に依存します。
それに関連して、すべてのメールフォーマットに有効な唯一のパターンはありません。
フォーマットに従わせたいルールを定義し、それに従ってパターンを構築する必要があるだけです。
新しいルールが増えるごとに、受け入れられるアドレスの自由度は下がっていきます。