メール検証


10

コメントを無視したり、空白()を折り畳んだり、一般化されたアドレスリテラルを緩和したりして、RFC 53215322に見られるいくつかの文法規則)に対して電子メールアドレスを検証する関数またはプログラムを記述しますCFWS。これは文法を与えます

Mailbox              = Local-part "@" ( Domain / address-literal )

Local-part           = Dot-string / Quoted-string
Dot-string           = Atom *("."  Atom)
Atom                 = 1*atext
atext                = ALPHA / DIGIT /    ; Printable US-ASCII
                       "!" / "#" /        ;  characters not including
                       "$" / "%" /        ;  specials.  Used for atoms.
                       "&" / "'" /
                       "*" / "+" /
                       "-" / "/" /
                       "=" / "?" /
                       "^" / "_" /
                       "`" / "{" /
                       "|" / "}" /
                       "~"
Quoted-string        = DQUOTE *QcontentSMTP DQUOTE
QcontentSMTP         = qtextSMTP / quoted-pairSMTP
qtextSMTP            = %d32-33 / %d35-91 / %d93-126
quoted-pairSMTP      = %d92 %d32-126

Domain               = sub-domain *("." sub-domain)
sub-domain           = Let-dig [Ldh-str]
Let-dig              = ALPHA / DIGIT
Ldh-str              = *( ALPHA / DIGIT / "-" ) Let-dig

address-literal      = "[" ( IPv4-address-literal / IPv6-address-literal ) "]"
IPv4-address-literal = Snum 3("."  Snum)
IPv6-address-literal = "IPv6:" IPv6-addr
Snum                 = 1*3DIGIT
                       ; representing a decimal integer value in the range 0 through 255

注:IPv6-addrこの特定のRFCは誤解を招き、egなどを許可しないため、の定義をスキップしました::1。正しい仕様はRFC 2373にあります。

制限事項

既存の電子メール検証ライブラリ呼び出しを使用することはできません。ただし、既存のネットワークライブラリを使用してIPアドレスを確認することもできます。

関数/メソッド/演算子/同等のものを記述する場合は、文字列を取得し、言語に応じてブール値または真/偽の値を返す必要があります。プログラムを作成する場合は、stdinから1行を取得し、終了コードを介して有効または無効を示す必要があります。

テストケース

次のテストケースは、簡潔にするためにブロックでリストされています。最初のブロックは合格するべきケースです:

email@domain.com
e@domain.com
firstname.lastname@domain.com
email@subdomain.domain.com
firstname+lastname@domain.com
email@123.123.123.123
email@[123.123.123.123]
"email"@domain.com
1234567890@domain.com
email@domain-one.com
_______@domain.com
email@domain.name
email@domain.co.jp
firstname-lastname@domain.com
""@domain.com
"e"@domain.com
"\@"@domain.com
email@domain
"Abc\@def"@example.com
"Fred Bloggs"@example.com
"Joe\\Blow"@example.com
"Abc@def"@example.com
customer/department=shipping@example.com
$A12345@example.com
!def!xyz%abc@example.com
_somename@example.com
_somename@[IPv6:::1]
fred+bloggs@abc.museum
email@d.com
?????@domain.com

次のテストケースはパスしません。

plainaddress
#@%^%#$@#$@#.com
@domain.com
Joe Smith <email@domain.com>
email.domain.com
email@domain@domain.com
.email@domain.com
email.@domain.com
email.email.@domain.com
email..email@domain.com
email@domain.com (Joe Smith)
email@-domain.com
email@domain..com
email@[IPv6:127.0.0.1]
email@[127.0.0]
email@[.127.0.0.1]
email@[127.0.0.1.]
email@IPv6:::1]
_somename@domain.com]
email@[256.123.123.123]

以来、IPv6-addr未定義のままに、アドレスとIPv6アドレスを持つテストケースがあるされている、それらを検証するための正しい方法は何ですか?
ardnew

なぜ必要があるemail@d.com?????@domain.com失敗しますか?
2013

1
@ardnew、関連するRFCへのリンクを追加しました。質問はすでにかなり長いので、インライン化したくありません。
Peter Taylor

@grc、いい質問です。質問がサンドボックスにあるという数か月の間に誰もこれを提起しなかったので、私はそれらをチェックしましたが、なぜ失敗するのかわからないので、「パス」側に移動しました。
Peter Taylor

長さ制限も必要ですか?メールアドレス全体で254 / local-partで64 /各ドメインラベルで63?
MichaelRushton 2013年

回答:


2

Python 3.3、261

import re,ipaddress
try:v,p=re.match(r'^(?!\.)(((^|\.)[\w!#-\'*+\-/=?^-~]+)+|"([ !#-[\]-~]|\\[ -~])*")@(((?!-)[a-zA-Z\d-]+(?<!-)($|\.))+|\[(IPv6:)?(.*)\])(?<!\.)$',input()).groups()[7:];exec("if p:ipaddress.IPv%dAddress(p)"%(v and 6or 4))
except:v=5
print(v!=5)

IPv4およびIPv6アドレスの検証に使用されるipaddressモジュールには、Python 3.3が必要です。

ゴルフレスバージョン:

import re, ipaddress

dot_string = r'(?!\.)((^|\.)[\w!#-\'*+\-/=?^-~]+)+'
    # negative lookahead to check that string doesn't start with .
    # each atom must start with a . or the beginning of the string

quoted_string = r'"([ !#-[\]-~]|\\[ -~])*"'
    # - is used for character ranges (also in dot_string)

domain = r'((?!-)[a-zA-Z\d-]+(?<!-)($|\.))+(?<!\.)'
    # negative lookahead/lookbehind to check each subdomain doesn't start/end with -
    # each domain must end with a . or the end of the string
    # negative lookbehind to check that string doesn't end with .

address_literal = r'\[(IPv6:)?(.*)\]'
    # captures the is_IPv6 and ip_address groups

final_regex = r'^(%s|%s)@(%s|%s)$' % (dot_string, quoted_string, domain, address_literal)

try:
    is_IPv6, ip_address = re.match(final_regex, input(), re.VERBOSE).groups()[7:]
        # if input doesn't match, calling .groups() will throw an exception

    if ip_address:
        exec("ipaddress.IPv%dAddress(ip_address)" % (6 if is_IPv6 else 4))
            # IPv4Address or IPv6Address will throw an exception if ip_address isn't valid
except:
    is_IPv6 = 5

print(is_IPv6 != 5)
    # is_IPv6 is used as a flag to tell whether an exception was thrown

非常に素晴らしい。重複するパターンをすぐに見つけることができません(より短い変数識別子に置き換えるため)。しかしALPHA、拡張BNFのように見え、a Quoted-stringを構成するcharリテラルはすべて大文字と小文字を区別しません。大文字と小文字を区別せずにこれらの文字クラス範囲の1つを破棄することで、いくつかの文字を剃ることができますか?ところで、もしあなたが気が動転しているなら、あなたがこれをどのように開発したかについて簡単に説明できますか?
ardnew

@ardnew:ありがとう。より複雑なバージョンを追加し、いくつかのコメントを付けて、よりトリッキーな部分を説明しようとしました。私は4つの個別の部分(ドット文字列、引用文字列、ドメイン、アドレスリテラル)で正規表現を開発し、それらをマージして、IP検証を追加しました。言うまでもないことですが、ゴルフをするのはとても面倒です。
2013

長さの制限はありませんか?
MichaelRushton 2013年

2

PHP 5.4.9、495

function _($e){return preg_match('/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!"?(?>\\\[ -~]|[^"]){65,}"?@)(?>([!#-\'*+\/-9=?^-~-]+)(?>\.(?1))*|"(?>[ !#-\[\]-~]|\\\[ -~])*")@(?!.*[^.]{64,})(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?2)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?3)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?3)(?>:(?3)){0,6})?::(?4)?))|(?>(?>IPv6:(?>(?3)(?>:(?3)){5}:|(?!(?:.*[a-f0-9]:){6,})(?5)?::(?>((?3)(?>:(?3)){0,4}):)?))?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?>\.(?6)){3}))\])$/iD', $e);}

そしてさらに興味があるために、ここにネストされたCFWSと廃止されたローカル部分を可能にするRFC 5322文法の1つを示します。

(764)

function _($e){return preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z\d-]{64,})(?1)(?>([a-z\d](?>[a-z\d-]*[a-z\d])?)(?>(?1)\.(?!(?1)[a-z\d-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f\d]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f\d][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f\d]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?>\.(?9)){3}))\])(?1)$/isD', $e);}

そして、長さ制限が要件ではない場合:

RFC 5321(414)

function _($e){return preg_match('/^(?>([!#-\'*+\/-9=?^-~-]+)(?>\.(?1))*|"(?>[ !#-\[\]-~]|\\\[ -~])*")@(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?2)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?3)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?3)(?>:(?3)){0,6})?::(?4)?))|(?>(?>IPv6:(?>(?3)(?>:(?3)){5}:|(?!(?:.*[a-f0-9]:){6,})(?5)?::(?>((?3)(?>:(?3)){0,4}):)?))?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?>\.(?6)){3}))\])$/iD', $e);}

RFC 5322(636)

function _($e){return preg_match('/^((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?1)(?>([a-z\d](?>[a-z\d-]*[a-z\d])?)(?>(?1)\.(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f\d]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f\d][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f\d]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?>\.(?9)){3}))\])(?1)$/isD', $e);}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.