E-Mail-Validierung

10

Schreiben Sie eine Funktion oder ein Programm, um eine E-Mail-Adresse anhand von RFC 5321 (einige Grammatikregeln in 5322 ) zu validieren, mit der Entspannung, dass Sie Kommentare und gefaltete Leerzeichen ( CFWS) und verallgemeinerte Adressliterale ignorieren können . Dies gibt die Grammatik

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

Hinweis: Ich habe die Definition von übersprungen, IPv6-addrweil dieser bestimmte RFC es falsch macht und z ::1. B. nicht zulässt . Die korrekte Spezifikation ist in RFC 2373 enthalten .

Beschränkungen

Sie dürfen keine vorhandenen Aufrufe der E-Mail-Validierungsbibliothek verwenden. Sie können jedoch vorhandene Netzwerkbibliotheken verwenden, um IP-Adressen zu überprüfen.

Wenn Sie eine Funktion / Methode / Operator / Äquivalent schreiben, sollte sie eine Zeichenfolge verwenden und je nach Sprache einen booleschen oder wahrheitsgemäßen / falschen Wert zurückgeben. Wenn Sie ein Programm schreiben, sollte es eine einzelne Zeile von stdin nehmen und über den Exit-Code gültig oder ungültig anzeigen.

Testfälle

Die folgenden Testfälle sind aus Gründen der Kompaktheit in Blöcken aufgeführt. Der erste Block sind Fälle, die bestehen sollten:

[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]

Die folgenden Testfälle sollten nicht bestanden werden:

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
quelle
IPv6-addrGibt es eine korrekte Möglichkeit, diese zu validieren, da sie nicht definiert wurde und es Testfälle mit IPv6-Adressen gibt?
Neu
Warum sollte [email protected]und [email protected]scheitern?
Grc
1
@ardnew, ich habe einen Link zum entsprechenden RFC hinzugefügt. Ich möchte es nicht einbinden, weil die Frage schon ziemlich lang ist.
Peter Taylor
@grc, gute Frage. Ich habe sie überprüft, weil dies in den Monaten, in denen sich die Frage im Sandkasten befand , niemand angesprochen hat , aber ich kann nicht verstehen, warum sie fehlschlagen sollten, also habe ich sie auf die Seite "Pass" verschoben.
Peter Taylor
Sind auch Längenbeschränkungen erforderlich? 254 für die gesamte E-Mail-Adresse / 64 für den lokalen Teil / 63 für jedes Domain-Label?
Michael

Antworten:

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 wird für das IP-Adressmodul benötigt, mit dem IPv4- und IPv6-Adressen überprüft werden.

Weniger Golfversion:

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
quelle
Sehr schön. Ich kann keine doppelten Muster sofort finden (um sie durch eine kürzere Variablenkennung zu ersetzen). aber es sieht so aus, als ob ALPHAin erweitertem BNF und den Zeichenliteralen, die a konstruieren, Quoted-stringdie Groß- und Kleinschreibung nicht berücksichtigt wird. Können Sie ein paar Zeichen rasieren, indem Sie die Groß- und Kleinschreibung nicht angeben und einen dieser Zeichenklassenbereiche fallen lassen? Übrigens, wenn Sie sich munter fühlen, können Sie kurz beschreiben, wie Sie dies entwickelt haben?
Neu
@ardnew: Danke. Ich habe eine weniger Golfversion mit ein paar Kommentaren hinzugefügt, um einige der schwierigeren Teile zu erklären. Ich habe den regulären Ausdruck in vier Einzelteilen (Punktzeichenfolge, Anführungszeichenfolge, Domäne und Adressliteral) entwickelt, sie dann zusammengeführt und die IP-Validierung hinzugefügt. Unnötig zu erwähnen, dass das Golfen sehr chaotisch wurde.
Grc
Keine Längenbeschränkungen?
Michael
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);}

Und nur für weiteres Interesse, hier ist eine für die RFC 5322-Grammatik, die verschachtelte CFWS und veraltete lokale Teile ermöglicht:

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

Und wenn Längenbeschränkungen keine Voraussetzung sind:

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);}
Michael Rushton
quelle