【正規表現】メールアドレスの正規表現フォーマットを読み解く

# テーマ
[Vue.js入門 基礎から実践アプリケーション開発まで](https://gihyo.jp/book/2018/978-4-297-10091-9)を読み進めていたらこんな正規表現に出会いました。

```javascript:regEx.js
const REGEX_EMAIL = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
```

これはメールアドレスのフォーマットをチェックする正規表現らしいのですが、ぱっと見でどんな表現をチェックしているのかわからなかったので正規表現の勉強もかねて読み解いてみました。
自分がどう読み解いていったかの記録を残していきたいと思います。

## JavaScriptにおける正規表現の生成
JavaScriptで正規表現を作成するには以下の2つの方法が存在します。今回の例ではリテラル表現による生成が行われていました。

### リテラル表現

```javascript
var str = /正規表現/オプション;
```

### RegExオブジェクトのコンストラクタ

```javascript
var str = new RegExp('正規表現','オプション');
```

https://qiita.com/iLLviA/items/b6bf680cd2408edd050f
の記事より引用しました。

今回の例も行頭と末尾をスラッシュで囲っています。スラッシュで囲まれた範囲内が実際の正規表現の中身です。

```javascript
^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
```

## 丸括弧によるグループ化
丸括弧`()`によって正規表現をグループ化できます。先ほどの正規表現をグループごとに分けていきます。

```javascript
^(([^<>()[\]\\.,;:\s@"]+ (\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))
@
((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
```

まず一回目の分解。`文字列＠文字列`に一致する文字列ということですね。
アットマークの前半と後半でグループが分かれていることがわかります。
それぞれローカル部とドメイン部の正規表現になっています。
ではさらに分解していきます。

## 行頭・末尾マッチ
`^(正規表現)`とするとその正規表現で行頭が一致するもの表します。
同様に`(正規表現)$`とするとその正規表現で末尾が一致するものを表します。

今回の例ではローカル部では行頭一致、ドメイン部は末尾一致が使われています。

```javascript:
^(([^<>()[\]\\.,;:\s@"]+ (\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))
```
⇒行頭が`([^<>()[\]\\.,;:\s@"]+ (\.[^<>()[\]\\.,;:\s@"]+)*)|(".+")`に一致

```javascript:
((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
```
⇒末尾が`(\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,})`に一致
どんどん分解していきます。

## パイプを利用したまたはの表現
`(正規表現１)|(正規表現２)`のように2つの正規表現の間にパイプ`|`を挟むと正規表現１または正規表現２という表現ができます。

```javascript:ローカル部
([^<>()[\]\\.,;:\s@"]+ (\.[^<>()[\]\\.,;:\s@"]+)*)
|
(".+")
```

```javascript:ドメイン部
(\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])
|
(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,})
```

## プラス、アスタリスクを利用した繰り返しの表現
`正規表現+`のように正規表現の末尾にプラス`+`をつけると正規表現に一致する文字の1回以上の繰り返しを意味します。
同様に`正規表現*`のように末尾にアスタリスク`*`をつけると正規表現に一致する文字の0回以上の繰り返しを意味します。

```javascript:ローカル部の前半の前半
[^<>()[\]\\.,;:\s@"]+ 
```
⇒`[^<>()[\]\\.,;:\s@"]`の0回以上の繰り返し
※`[^<>()[\]\\.,;:\s@"]`はメールアドレスに利用できない文字とのこと

```javascript:ローカル部の前半の後半
(\.[^<>()[\]\\.,;:\s@"]+)*
```
⇒`\.[^<>()[\]\\.,;:\s@"]+`の1回以上の繰り返し、ですがここは少し複雑ですね。繰り返す対象の正規表現にも繰り返しの`+`が使われています。ここは次の章でもう少し深く見ていきます。

```javascript:ドメイン部の後半の前半
([a-zA-Z\-0-9]+\.)+ 
```
⇒`[a-zA-Z\-0-9]+\.`の0回以上の繰り返し。こちらも先ほど同様に繰り返し対象の正規表現にも繰り返しの`+`が使われているようです。

ずいぶん細かく分解できました。ゴールはあと少しです。

## いずれかの文字に一致、エスケープ表現
角括弧`[]`の間に文字を挟むと角括弧内のいずれかの文字に一致する、を意味する正規表現になります。
`[^]`のように各括弧に続いて山形記号`^`を続けると各括弧内のいずれの文字にも一致しない、を意味します。

また、先ほどは`\.`のような表現もありました。
これはエスケープ表現です。`.`だけだと「任意の一文字」を意味する正規表現になります。バックスラッシュ`\`を前につけることで文字列のドット`.`を意味することができます。

```javascript:ローカル部の前半の前半
[^<>()[\]\\.,;:\s@"]
```
⇒`<>()[\]\\.,;:\s@"`のいずれの文字にも一致しない。つまりはメールアドレスのローカル部に使えない文字ということですね。

```javascript:ローカル部の前半の後半
\.[^<>()[\]\\.,;:\s@"]+
```
⇒文字列ドットに続き`<>()[\]\\.,;:\s@"`のいずれにも一致しない文字が1回以上繰り返される文字列に一致

これでローカル部はすべて分解して理解することができましたね。ドメイン部もどんどん分解していきます。

## 範囲指定、繰り返し回数指定
`[0-9]`とすることで0から9までの文字いずれかを表現することができます。
同様に`[A-Z]`でAからZまでのいずれかの大文字英字のいずれか、`[a-z]`でaからzまでの小文字英字いずれかを意味します。

また波括弧`{}`直前の文字を何回繰り返すかを指定できます。
`{n}`とすれば直前の文字のn回の繰り返した文字列に一致
`{n,}`とすれば直前の文字のn回以上の繰り返した文字列に一致
`{n,m}`とすれば直前の文字のn回以上m回内繰り返した文字列に一致
となります。

```javascript:ドメイン部の前半
(\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])
```
⇒角括弧`[`、0から9間までの数字の1つ以上3つ以内、ドット`.`、0から9間までの数字の1つ以上3つ以内、ドット`.`、0から9間までの数字の1つ以上3つ以内、ドット`.`、0から9間までの数字の1つ以上3つ以内、角括弧閉じ`]`に一致
⇒ドメイン部のこのチェックはどのような意味があるのでしょうか。ご存じの方いらっしゃいましたらコメントください。

```javascript:ドメイン部の後半の前半
[a-zA-Z\-0-9]+\.
```
⇒aからz、AからZ、0から9の文字の1回以上の繰り返しにドット`.`が続く文字列に一致

```javascript:ドメイン部の後半の後半
[a-zA-Z]{2,}
```
⇒aからz、AからZの文字2回以上の繰り返しに一致。

## ダブルクオーテーションで囲まれたメールアドレス
ちなみに
```javascript:ローカル部の後半
(".+")
```
についてみていなかったのですが、
これはダブルクォーテーション、任意の1文字の1回以上の繰り返し、ダブルクォーテーション、となります。
メールアドレスのローカル部のルールに合わない場合はダブルクオーテーションで囲うことがあるそうです。

# まとめ
改めて今回みた正規表現は

```javascript:regEx.js
const REGEX_EMAIL = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
```
でした。

ここまで分解して理解内容をもとに翻訳すると

```
メールアドレス禁止文字以外の文字1つ以上.メールアドレスの禁止文字以外の文字1つ以上の0回以上の繰り返し
または
"任意の文字1回以上の繰り返し"

半角アットマーク

[0から9までの文字1回以上3回以内.0から9までの文字1回以上3回以内.0から9までの文字1回以上3回以内.0から9までの文字1回以上3回以内]
または
aからz、半角ハイフン、AからZ、0から9までの文字1つ以上.aからz、AからZの文字2つ以上
```
の文字列、ということになります。

正規表現をちゃんと調べたことなかったのでいい経験になりました。一見複雑に見えてもちゃんと見ていけばシンプルな要素で構成されているんですね。
こう見るとメールアドレスの本質というのも見えてきました。ほかの正規表現をこれから見ていきたいです。