2007年10月19日金曜日

PHPで(できるだけ)正しいメールアドレスをチェックする正規表現

2008/02/07 修正 (

  • Blogger投稿時に、バックスラッシュ2つが1つに変換されていた(全角で掲載することにより回避。全角の¥は半角に読み替えてください!)
  • domainの先頭は数字でもOKにした(ドメイン制限の緩和に合わせた)
  • domainのlabelの最後に?を追加(漏れていた)



何回か挫折したけどもう一度チャレンジしてみた。
RFC 2822を読み解くのは辛いので、基準として「正しい方法」でメールアドレスを確認するには - J0hn D0e の日誌に書いてある『「正しいメールアドレスの条件」10か条』をなるべく満たすことを目指した。

できたのはこれ。
preg_match('/^([-!#-¥¥'*+¥¥/-9=?^-~]+(¥¥.[-!#-¥¥'*+¥¥/-9=?^-~]+)*|"([]-~!#-[]|¥¥¥¥[ -~])*")@[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?(¥¥.[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?)*¥¥.([a-z]{2,4}|museum)$/i', $value)


10か条のうち、1~4と6~8を満たしている(と思う)。
以下は未対応
  • 10は正規表現では無理
  • 5と9も正規表現では無理だよね?
  • 3(quoted-string)については、RFC 2822を見た感じではlocal-part全体がquoted-stringの場合のみ使えるように見えるけど、symfonyではlocal-partの一部としてもquoted-stringを許しているのは何故?
  • 4(quoted-pair)については、RFC 2822を見た感じではquoted-string内でのみ使えるように見えるけど、「正しい方法」でメールアドレスを確認するにはを読んだ感じだとlocal-part内ならどこでも使えるみたいに書いてあるからそうなのかな???

現実問題としては、DoCoMo(Softbankも?)はRFC2822違反のメールアドレスでも登録できてしまうので、下記のような"DoCoMo版メールアドレス"は上記の正規表現ではエラーになってしまう。
  • ドットで始まる
  • ドットが連続する
  • @の前がドット


ついでに、上記の正規表現を生成したPHPのソースコードはこちら。
(コメントの番号は10か条の番号に対応)
<?php
if (!$m = $_REQUEST['m']) $m = 'test@example.com';

//local part
$atext = "[-!#-'*+/-9=?^-~]";
$atom = $atext . '+';
$dot_atom = $atom . '(¥¥.' . $atom . ')*'; //2

$qtext = '[]-~!#-[]';
$text = '[ -~]';
$quoted_pair = '¥¥¥¥' . $text; //4
$quoted_string = '"(' . $qtext . '|' . $quoted_pair . ')*"'; //3

$local_part = '(' . $dot_atom . '|' . $quoted_string . ')';

//domain
$label = '[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?'; //7, 8
$domain = $label . '(¥¥.' . $label . ')*¥¥.([a-z]{2,4}|museum)'; //6

//全体
$addr_spec = $local_part . '@' . $domain; //1

$regexp = '/^' . str_replace('/', '¥¥/', $addr_spec) . '$/i';
?>

正規表現<br />
<?php echo $regexp; ?>
<hr />

preg_match('<strong><?php echo str_replace(array("'", '¥¥'), array("¥¥'", '¥¥¥¥'), $regexp); ?></strong>', $value)
<hr />

<form action="regex.php">
<input name="m" value="<?php echo htmlSpecialChars($m, ENT_QUOTES); ?>" />
は、正し<?php echo (preg_match($regexp, $m) ? 'い' : 'くない') ?>メールアドレスです。
</form>


一般的には、これくらいやれば十分でしょう。
preg_match('/^[-+¥¥w]+(¥¥.[-+¥¥w]+)*@[-a-z0-9]+(¥¥.[-a-z0-9]+)*¥¥.[a-z]{2,6}$/i', $value)


DoCoMoも救済するならもう少しシンプルになる。実際に使うならこれで。
local-partに入れる記号は"-_."くらいが一般的で、Gmailerは"+"も使うからそれぐらい対応すれば99%くらいはカバーすると思う。これでダメなメールアドレスは弾いちゃった方が何かと安心な気がする...。
preg_match('/^[-+.¥¥w]+@[-a-z0-9]+(¥¥.[-a-z0-9]+)*¥¥.[a-z]{2,6}$/i', $value)


参考:
 「正しい方法」でメールアドレスを確認するには - J0hn D0e の日誌
 http://www.puni.net/~mimori/rfc/rfc2822.txt (Internet Message Format)
 ASCII文字コード : IT用語辞典
 正規表現
 perlre - Perlの正規表現
 re: PHPでメールアドレスかどうか調べる方法 (ハズレ日記)

2 件のコメント:

匿名 さんのコメント...

.travelが通らない気がします。

floatingdays さんのコメント...

返事が遅れてすみません。

travelなら6文字なので通ると思うのですが...

ブログ アーカイブ

tags