Validasi email

10

Tulis fungsi atau program untuk memvalidasi alamat email terhadap RFC 5321 (beberapa aturan tata bahasa ditemukan di 5322 ) dengan relaksasi yang dapat Anda abaikan komentar dan lipat spasi putih ( CFWS) dan literal alamat umum. Ini memberi tata bahasa

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

Catatan: Saya telah melewatkan definisi IPv6-addrkarena RFC khusus ini salah dan tidak mengizinkan misalnya ::1. Spesifikasi yang benar ada di RFC 2373 .

Batasan

Anda tidak boleh menggunakan panggilan pustaka validasi email yang ada. Namun, Anda dapat menggunakan perpustakaan jaringan yang ada untuk memeriksa alamat IP.

Jika Anda menulis fungsi / metode / operator / setara, ia harus mengambil string dan mengembalikan nilai boolean atau truey / falsy, yang sesuai untuk bahasa Anda. Jika Anda menulis sebuah program, ia harus mengambil satu baris dari stdin dan menunjukkan valid atau tidak valid melalui kode keluar.

Uji kasus

Kasing uji berikut tercantum dalam blok untuk kekompakan. Blok pertama adalah case yang harus dilewati:

[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
email@[123.123.123.123]
"email"@domain.com
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
""@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/[email protected]
[email protected]
!def!xyz%[email protected]
[email protected]
_somename@[IPv6:::1]
[email protected]
[email protected]
[email protected]

Kasus uji berikut tidak boleh lulus:

plainaddress
#@%^%#$@#$@#.com
@domain.com
Joe Smith <[email protected]>
email.domain.com
email@[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected] (Joe Smith)
[email protected]
[email protected]
email@[IPv6:127.0.0.1]
email@[127.0.0]
email@[.127.0.0.1]
email@[127.0.0.1.]
email@IPv6:::1]
[email protected]]
email@[256.123.123.123]
Peter Taylor
sumber
sejak IPv6-addrdibiarkan tidak terdefinisi, dan ada kasus uji yang memiliki alamat ipv6, apakah ada cara yang benar untuk memvalidasinya?
ardnew
Kenapa harus [email protected]dan [email protected]gagal?
grc
1
@ardnew, saya telah menambahkan tautan ke RFC yang relevan. Saya tidak ingin memasukkannya karena pertanyaannya sudah cukup lama.
Peter Taylor
@ grc, pertanyaan bagus. Saya sudah memeriksanya, karena tidak ada yang mengajukan ini selama beberapa bulan bahwa pertanyaannya ada di kotak pasir , tapi saya tidak bisa melihat mengapa mereka gagal sehingga saya memindahkan mereka ke sisi "Lulus".
Peter Taylor
Apakah batas panjang juga diperlukan? 254 untuk seluruh alamat email / 64 untuk bagian-lokal / 63 untuk setiap label domain?
MichaelRushton

Jawaban:

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)

Python 3.3 diperlukan untuk modul ipaddress, yang digunakan untuk memvalidasi alamat IPv4 dan IPv6.

Versi yang kurang golf:

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
grc
sumber
sangat bagus. saya tidak dapat segera menemukan pola duplikat (untuk mengganti dengan pengenal variabel yang lebih pendek). tapi sepertinya ALPHAdi augmented BNF dan char literals yang membangun Quoted-stringsemua case-insensitive. dapatkah Anda mencukur beberapa karakter dengan menentukan case-insensitivity dan membuang salah satu rentang kelas char? btw, jika kamu merasa lincah, dapatkah kamu memberikan deskripsi singkat tentang bagaimana kamu mengembangkan ini?
ardnew
@ardnew: Terima kasih. Saya telah menambahkan versi yang kurang golf dengan beberapa komentar yang mencoba menjelaskan beberapa bagian yang lebih sulit. Saya mengembangkan regex dalam empat bagian individu (dot-string, quote-string, domain dan address-literal), kemudian menggabungkannya bersama dan menambahkan validasi ip. Tak perlu dikatakan, golf itu menjadi sangat berantakan.
grc
Tidak ada batasan panjang?
MichaelRushton
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);}

Dan hanya untuk kepentingan lebih lanjut, inilah satu untuk tata bahasa RFC 5322 yang memungkinkan CFWS bersarang dan komponen lokal yang sudah usang:

(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);}

Dan jika batas panjang bukan persyaratan:

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);}
MichaelRushton
sumber