HTML 仕様のメールアドレス正規表現に最小限の修正を加える


TL;DR

以下の正規表現は、HTML 仕様のメールアドレス正規表現の末尾に、 TLD 必須のルールを加えたものです。

/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$/

<input type="email">pattern 属性に指定する場合は、以下のように短く書けます。

<input type="email" pattern=".+\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]">

以下、詳細です。

HTML 仕様のメールアドレス正規表現について

HTML 仕様には、 <input type="email"> のバリデーションルールと同等の正規表現が含まれています。

以下がその正規表現です。

/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/

ブラウザが標準で提供する機能なので、ぜひこのルールを活用したいところです。

課題

しかしながら。。

この type="email" は、TLD が無いメールアドレスを許可しますa@a は、valid なアドレスだと判定されます。

上の正規表現の遷移図は以下のようになっています。


(Regexper)

このような緩いルールになっている理由は、pattern 属性を使って更に条件を狭めることができるからです。デフォルトでメールアドレスとして最も緩い条件を適用し、次に pattern で必要に応じた制限を加えます。

If you need the entered e-mail address to be restricted further than just "any string that looks like an e-mail address," you can use the pattern attribute to specify a regular expression the value must match for it to be valid.
(MDN)

デフォルトの正規表現と pattern の正規表現は、AND 条件です。両方が検証されます。

However, the browser runs both the standard e-mail address filter and our custom pattern against the specified text.
(MDN)

2つのルールは AND で検証されるので、pattern 属性で、例えば @ が含まれるかどうかをチェックする必要はありません。

pattern 属性を指定しない type="email" は、現実には使い物になりません。TLD があっても無くてもどちらでも構わないようなメールアドレスを input で入力させるのは、相当に特殊な場合だけでしょう。

JPNIC の「ドットなしドメイン名 (Dotless Domain Names) について」の記事も参考になります。

正規表現に TLD の制限を加える

そこで、HTML 仕様を極力変えずに TLD の制限を加えてみます。

(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*

がドメインの2番め以降のラベルで、0回以上の繰り返しなので、この後に、以下を付け加えます。

\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]

1文字の TLD を禁止する仕様は存在しないようですが、今の TLD に1文字のものはないので、(?: )? を外して「TLD 2 文字以上」の制限を加えています。上限 63 文字は、他のラベルと同様です。

修正を加えた正規表現は以下の通り。

/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$/

遷移図は以下のようになります。


(Regexper)

TLD 部分だけを pattern に指定できるか

通常の正規表現であれば、部分一致で、末尾の TLD の正規表現だけを指定できそうなものです。

pattern="\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$"

しかし、これはうまくいきません。HTML の pattern 属性は、^(?: )$ でラップされるからです。

5 . Let anchoredPattern be the string "^(?:", followed by pattern, followed by ")$".
(HTML Living Standard)

なので、pattern にはメールアドレス全体にマッチする正規表現を指定する必要があります。

.+ を先頭につけて、

pattern=".+\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]"

と書けます。デフォルトのルールと pattern は両方が検証されるので、前半部分は .+ と省略してしまっても問題ありません。

ローカルパートに記号を許すか

デフォルトの正規表現では、以下の記号をローカルパート (@ より前) に許可しています。

.!#$%&'*+/=?^_`{|}~-

この内、絶対に必要なのは以下でしょう。

.+_-

残りの記号は、場合によっては削ってもよいかもしれません。

!#$%&'*/=?^`{|}~

特にユーザ登録で、他のサービスと連携する可能性がある場合、最初から絞っておかないと後で困る可能性がありそうです。こちらで許可した記号が、連携先では許可されていないかもしれません。

メジャーなサービスのバリデーションはどうなっているか

いくつかのサイトで、ユーザ登録画面の挙動を見てみました。(実際には登録せず、クライアントサイドのバリデーションの挙動だけを見ています)

  • Facebook は、1 文字の TLD は許可。ローカルパートの記号も許可しているようでした
  • Twitter は、1文字の TLD は禁止。記号は基本の文字に加えて & を許可していました
  • Apple ID は、1文字の TLD は禁止。基本の記号だけを許可しているようでした

デモ

以下type="email"input 要素に pattern を指定したときの挙動を試せます。

See the Pen HTML email validation by koseki (@kkoseki) on CodePen.

また、様々なメールアドレスに正規表現を適用するテストページをこちらに作成しました。

Microsoft の以下のページに、いい感じのテストデータがあります。