이메일 검증


10

주석을 무시하고 공백 ( ) 및 일반화 된 주소 리터럴을 무시할 수있는 완화 기능을 사용하여 RFC 5321 (일부 문법 규칙 5322 에서 발견됨)에 대해 전자 우편 주소의 유효성을 검증하는 함수 또는 프로그램을 작성하십시오 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가 잘못되어 예를 들어 허용하지 않기 때문에 정의를 건너 뛰었습니다 ::1. 올바른 사양은 RFC 2373에 있습니다.

제한 사항

기존 이메일 유효성 검사 라이브러리 호출을 사용할 수 없습니다. 그러나 기존 네트워크 라이브러리를 사용하여 IP 주소를 확인할 수 있습니다.

함수 / 메소드 / 연산자 / 동등한 함수를 작성하는 경우 언어에 따라 문자열을 가져 와서 부울 값 또는 진실 / 거짓 값을 반환해야합니다. 프로그램을 작성하는 경우 stdin에서 한 줄을 가져 와서 종료 코드를 통해 유효 또는 무효를 표시해야합니다.

테스트 사례

다음 테스트 사례는 압축을 위해 블록으로 나열됩니다. 첫 번째 블록은 다음과 같은 경우입니다.

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실패?
grc

1
@ardnew, 관련 RFC에 대한 링크를 추가했습니다. 질문이 이미 너무 길어서 인라인하고 싶지 않습니다.
피터 테일러

@ grc, 좋은 질문입니다. 나는 몇 달 동안 아무도 샌드 박스에 질문을 제기하지 않았기 때문에 그것들을 확인 했지만, 왜 실패 해야하는지 알 수 없으므로 그들을 "Pass"쪽으로 옮겼습니다.
피터 테일러

길이 제한도 필요합니까? 각 도메인 레이블의 local-part / 63에 대한 전체 이메일 주소 / 64는 254?
MichaelRushton

답변:


2

파이썬 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를 구성하는 문자 리터럴 은 모두 대소 문자를 구분하지 않습니다. 대소 문자를 구분하지 않고 해당 문자 클래스 범위 중 하나를 버림으로써 몇 가지 문자를 면도 할 수 있습니까? btw, 당신이 거친 느낌이 든다면, 이것을 어떻게 개발했는지에 대한 간단한 설명을 줄 수 있습니까?
ardnew

@ardnew : 감사합니다. 좀 까다로운 부분을 설명하려고 몇 가지 의견으로 덜 골프 버전을 추가했습니다. 나는 정규 표현식을 4 개의 개별 조각 (dot-string, quoted-string, domain 및 address-literal)으로 개발 한 다음 함께 병합하여 ip 유효성 검사를 추가했습니다. 말할 것도없이, 골프는 정말 지저분 해졌다.
grc

길이 제한이 없습니까?
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);}

그리고 추가 관심을 끌기 위해 중첩 CFWS 및 더 이상 사용되지 않는 로컬 부분을 허용하는 RFC 5322 문법에 대한 것이 있습니다.

(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.