Der reguläre Ausdruck, zu dem ich das meiste Feedback erhalte, ganz zu schweigen von „Fehler“-Meldungen, ist der, den Sie direkt auf der Startseite dieser Website finden: \b+@+\.{2,}\b. Dieser reguläre Ausdruck, so behaupte ich, passt auf jede E-Mail-Adresse. Die meisten Rückmeldungen, die ich erhalte, widerlegen diese Behauptung, indem sie eine E-Mail-Adresse zeigen, auf die dieser Regex nicht passt. Normalerweise enthält der „Fehler“-Bericht auch einen Vorschlag, um die Regex „perfekt“ zu machen.
Wie ich weiter unten erkläre, gilt meine Behauptung nur, wenn man meine Definition akzeptiert, was eine gültige E-Mail-Adresse wirklich ist und was nicht. Wenn Sie eine andere Definition verwenden wollen, müssen Sie die Regex anpassen. Der Abgleich einer gültigen E-Mail-Adresse ist ein perfektes Beispiel dafür, dass man (1) vor dem Schreiben einer Regex genau wissen muss, was man abgleichen will und was nicht; und (2) dass es oft einen Kompromiss gibt zwischen dem, was genau ist, und dem, was praktisch ist.
Der Vorteil meines obigen regulären Ausdrucks ist, dass er auf 99 % der heute verwendeten E-Mail-Adressen passt. Alle E-Mail-Adressen, auf die er zutrifft, können von 99% aller E-Mail-Software verarbeitet werden, die es gibt. Wenn Sie nach einer schnellen Lösung suchen, müssen Sie nur den nächsten Absatz lesen. Wenn Sie alle Kompromisse kennen wollen und viele Alternativen zur Auswahl haben, lesen Sie weiter.
Wenn Sie den obigen regulären Ausdruck verwenden wollen, gibt es zwei Dinge, die Sie verstehen müssen. Erstens machen lange Regexe es schwierig, Absätze schön zu formatieren. Deshalb habe ich a-z in keine der drei Zeichenklassen aufgenommen. Diese Regex ist dafür gedacht, mit der aktivierten Option „case insensitive“ Ihrer Regex-Engine verwendet zu werden. (Sie wären überrascht, wie viele „Fehler“-Meldungen ich darüber bekomme.) Zweitens ist die obige Regex mit Wortgrenzen begrenzt, was sie für das Extrahieren von E-Mail-Adressen aus Dateien oder größeren Textblöcken geeignet macht. Wenn Sie prüfen wollen, ob der Benutzer eine gültige E-Mail-Adresse eingegeben hat, ersetzen Sie die Wortgrenzen durch Anker am Anfang und Ende der Zeichenkette, etwa so: ^+@+\.{2,}$.
Der vorherige Absatz gilt auch für alle folgenden Beispiele. Möglicherweise müssen Sie Wortgrenzen in Start/Ende-der-Zeichenkette-Anker umwandeln, oder umgekehrt. Und Sie müssen die Option „Groß-/Kleinschreibung nicht berücksichtigen“ einschalten.
Gegensätze bei der Validierung von E-Mail-Adressen
Bevor die ICANN es jedem finanzstarken Unternehmen ermöglichte, eigene Top-Level-Domains zu erstellen, waren die längsten Top-Level-Domains die selten verwendeten .museum und .travel, die 6 Buchstaben lang sind. Die gebräuchlichsten Top-Level-Domains waren 2 Buchstaben lang für länderspezifische Domains und 3 oder 4 Buchstaben lang für Allzweck-Domains wie .com und .info. Viele Regexe zur Überprüfung von E-Mail-Adressen, die Sie in verschiedenen Regex-Tutorials und -Referenzen finden, gehen immer noch davon aus, dass die Top-Level-Domain relativ kurz ist. Ältere Ausgaben dieses Regex-Tutorials erwähnen in der Einleitung \b+@+\.{2,4}\b als Regex für E-Mail-Adressen. Es gibt nur einen kleinen Unterschied zwischen dieser Regex und der oben auf dieser Seite. Die 4 am Ende der Regex schränkt die Top-Level-Domain auf 4 Zeichen ein. Wenn Sie diese Regex mit Ankern verwenden, um die in Ihrem Bestellformular eingegebene E-Mail-Adresse zu validieren, muss [email protected] seine Einkäufe woanders erledigen. Ja, die TLD .solutions existiert, und wenn ich dies schreibe, kann disaproved.solutions für $16,88 pro Jahr Ihnen gehören.
Wenn Sie strenger sein wollen als {2,} für die Top-Level-Domain, ist ^+@+\.{2,63}$ so weit, wie Sie praktisch gehen können. Jeder Teil eines Domainnamens darf nicht länger als 63 Zeichen sein. Es gibt keine einstelligen Top-Level-Domains und keine enthält Ziffern. Es sieht auch nicht so aus, als würde die ICANN solche Domains genehmigen.
E-Mail-Adressen können auf Servern auf einer Subdomain liegen, wie in [email protected]. Alle obigen Regexe passen zu dieser E-Mail-Adresse, weil ich in der Zeichenklasse nach dem @-Zeichen einen Punkt eingefügt habe. Aber die obigen Regexe passen auch auf john@aol…com, was wegen der aufeinanderfolgenden Punkte nicht gültig ist. Sie können solche Übereinstimmungen ausschließen, indem Sie +\. durch (?:+\.)+ in einer der obigen Regexes ersetzen. Ich habe den Punkt aus der Zeichenklasse entfernt und stattdessen die Zeichenklasse und den folgenden literalen Punkt wiederholt. Z.B. ^+@(?:+\.)+{2,}$ passt auf [email protected], aber nicht auf john@aol…com.
Wenn Sie vermeiden wollen, dass Ihr System bei beliebig großen Eingaben erstickt, können Sie die unendlichen Quantoren durch endliche ersetzen. ^{1,64}@(?:{1,63}\.){1,125}{2,63}$ berücksichtigt, dass der lokale Teil (vor dem @) auf 64 Zeichen und jeder Teil des Domainnamens auf 63 Zeichen begrenzt ist. Es gibt keine direkte Begrenzung für die Anzahl der Subdomains. Aber die maximale Länge einer E-Mail-Adresse, die von SMTP verarbeitet werden kann, beträgt 254 Zeichen. Mit einem einstelligen lokalen Teil, einer zweistelligen Top-Level-Domain und einstelligen Subdomains ist also 125 die maximale Anzahl von Subdomains.
Die vorherige Regex begrenzt E-Mail-Adressen nicht wirklich auf 254 Zeichen. Wenn jeder Teil seine maximale Länge hat, kann die Regex Zeichenfolgen mit einer Länge von bis zu 8129 Zeichen abgleichen. Sie können das reduzieren, indem Sie die Anzahl der erlaubten Subdomains von 125 auf etwas Realistischeres wie 8 senken. Ich habe noch nie eine E-Mail-Adresse mit mehr als 4 Subdomains gesehen. Wenn Sie das Limit von 254 Zeichen erzwingen wollen, ist die beste Lösung, die Länge der Eingabezeichenfolge zu prüfen, bevor Sie überhaupt eine Regex verwenden. Dies erfordert zwar ein paar Zeilen prozeduralen Code, aber die Überprüfung der Länge einer Zeichenkette erfolgt fast sofort. Wenn Sie nur Regexe verwenden können, kann ^{6,254}$ als erster Durchgang verwendet werden, um sicherzustellen, dass die Zeichenfolge keine ungültigen Zeichen enthält und nicht zu kurz oder zu lang ist. Wenn Sie alles mit einer Regex machen müssen, benötigen Sie eine Regex-Variante, die Lookahead unterstützt. Der reguläre Ausdruck ^(?={6,254}$){1,64}@(?:{1,63}\.){1,8}{2,63}$ verwendet einen Lookahead, um zunächst zu prüfen, ob die Zeichenfolge keine ungültigen Zeichen enthält und nicht zu kurz oder zu lang ist. Wenn der Lookahead erfolgreich ist, macht der Rest der Regex einen zweiten Durchlauf über die Zeichenkette, um die korrekte Platzierung des @-Zeichens und der Punkte zu überprüfen.
Alle diese Regexe erlauben die Zeichen ._%+- an beliebiger Stelle im lokalen Teil. Sie können erzwingen, dass der Lokalteil mit einem Buchstaben beginnt, indem Sie ^{0,63} statt ^{1,64} für den Lokalteil verwenden: ^{0,63}@(?:{1,63}\.){1,125}{2,63}$. Wenn Sie Lookahead verwenden, um die Gesamtlänge der Adresse zu prüfen, kann das erste Zeichen im Lookahead geprüft werden. Wir brauchen die Prüfung des ersten Zeichens nicht zu wiederholen, wenn wir die Länge des lokalen Teils prüfen. Diese Regex ist zu lang, um in die Breite der Seite zu passen, also schalten wir den Modus für freie Abstände ein:
^(?={5,253}$)
{1,64}@(?:{1,63}\.){1,8}{2,63}$
Domainnamen können Bindestriche enthalten. Sie können aber nicht mit einem Bindestrich beginnen oder enden. (?:{0,62})? passt zu einem Domain-Namen, der zwischen 1 und 63 Zeichen lang ist und mit einem Buchstaben oder einer Ziffer beginnt und endet. Die nicht-erfassende Gruppe macht die Mitte der Domain und den letzten Buchstaben oder die letzte Ziffer als Ganzes optional, um sicherzustellen, dass wir einstellige Domains zulassen und gleichzeitig sicherstellen, dass Domains mit zwei oder mehr Zeichen nicht mit einem Bindestrich enden. Die gesamte Regex fängt an, ziemlich kompliziert zu werden:
^{0,63}@
(?:(?:{0,62})?\.){1,8}{2,63}$
Domänennamen dürfen keine aufeinanderfolgenden Bindestriche enthalten. +(?:-+)* passt auf einen Domainnamen, der mit einem Buchstaben oder einer Ziffer beginnt und endet und eine beliebige Anzahl von nicht aufeinanderfolgenden Bindestrichen enthält. Dies ist die effizienteste Methode. Diese Regex führt kein Backtracking durch, um einen gültigen Domänennamen zu finden. Sie passt auf alle Buchstaben und Ziffern am Anfang des Domänennamens. Wenn keine Bindestriche vorhanden sind, schlägt die optionale Gruppe, die folgt, sofort fehl. Wenn es Bindestriche gibt, passt die Gruppe auf jeden Bindestrich, gefolgt von allen Buchstaben und Ziffern bis zum nächsten Bindestrich oder dem Ende des Domänennamens. Wir können die maximale Länge nicht erzwingen, wenn Bindestriche mit einem Buchstaben oder einer Ziffer gepaart werden müssen, aber Buchstaben und Ziffern können für sich alleine stehen. Aber wir können die Lookahead-Technik verwenden, die wir zum Erzwingen der Gesamtlänge der E-Mail-Adresse verwendet haben, um die Länge des Domainnamens zu erzwingen, während wir aufeinanderfolgende Bindestriche nicht zulassen: (?={1,63}\.)+(?:-+)*. Beachten Sie, dass der Lookahead auch auf den Punkt prüft, der nach dem Domainnamen erscheinen muss, wenn dieser in einer E-Mail-Adresse voll qualifiziert ist. Dies ist wichtig. Ohne die Prüfung auf den Punkt würde der Lookahead längere Domänennamen akzeptieren. Da der Lookahead den Text, auf den er passt, nicht verbraucht, wird der Punkt nicht in die Gesamtübereinstimmung dieser Regex einbezogen. Wenn wir diese Regex in die Gesamt-Regex für E-Mail-Adressen einfügen, wird der Punkt wie in den vorherigen Regexen übereinstimmen:
^{0,63}@
(?:(?={1,63}\)+(?:-+)*\.){1,8}{2,63}$
Wenn wir den Lookahead einbeziehen, um die Gesamtlänge zu überprüfen, macht unser Regex zwei Durchgänge über den lokalen Teil und drei Durchgänge über die Domainnamen, um alles zu validieren:
^(?={5,253}$){1,64}@
(?:(?={1,63}\.)+(?:-+)*\.){1,8}{2,63}$
Auf einem modernen PC oder Server wird diese Regex bei der Überprüfung einer einzelnen E-Mail-Adresse mit 254 Zeichen gut funktionieren. Die Ablehnung längerer Eingaben wäre sogar schneller, weil die Regex fehlschlägt, wenn die Vorausschau beim ersten Durchlauf fehlschlägt. Aber ich würde nicht empfehlen, eine so komplexe Regex zu verwenden, um in einem großen Archiv von Dokumenten oder Korrespondenz nach E-Mail-Adressen zu suchen. Sie sind besser dran, wenn Sie die einfache Regex oben auf dieser Seite verwenden, um schnell alles zu sammeln, was wie eine E-Mail-Adresse aussieht. Deduplizieren Sie die Ergebnisse und verwenden Sie dann eine strengere Regex, wenn Sie ungültige Adressen weiter herausfiltern wollen.
Apropos Rückverfolgung: Keine der Regexen auf dieser Seite führt eine Rückverfolgung durch, um gültige E-Mail-Adressen zu finden. Aber besonders die letztgenannten Regexen können bei ungültigen E-Mail-Adressen ein ziemliches Backtracking durchführen. Wenn Ihre Regex-Variante possessive Quantoren unterstützt, können Sie alle Rückverfolgungen eliminieren, indem Sie alle Quantoren possessiv machen. Da kein Backtracking erforderlich ist, um Übereinstimmungen zu finden, ändert dies nichts daran, was von diesen Regexen abgeglichen wird. Es erlaubt ihnen nur, schneller zu scheitern, wenn die Eingabe keine gültige E-Mail Adresse ist. Unsere einfachste Regex wird dann zu ^++@++\.{2,}+$ mit einem zusätzlichen + nach jedem Quantifizierer. Dasselbe können wir mit unserer komplexesten Regex machen:
^(?={5,253}+$){1,64}+@
(?:(?={1,63}+\.)++(?:-++)*+\.){1,8}+{2,63}+$
Ein wichtiger Kompromiss bei all diesen Regexen ist, dass sie nur englische Buchstaben, Ziffern und die am häufigsten verwendeten Sonderzeichen zulassen. Der Hauptgrund dafür ist, dass ich meiner E-Mail-Software nicht zutraue, mit viel mehr umgehen zu können. Auch wenn John.O’[email protected] eine syntaktisch gültige E-Mail-Adresse ist, besteht die Gefahr, dass manche Software das Apostroph als trennendes Anführungszeichen fehlinterpretiert. Das blinde Einfügen dieser E-Mail-Adresse in eine SQL-Abfrage wird im besten Fall dazu führen, dass diese fehlschlägt, wenn Strings mit einfachen Anführungszeichen begrenzt werden, und im schlimmsten Fall Ihre Website für SQL-Injection-Angriffe öffnen.
Und natürlich ist es schon seit vielen Jahren so, dass Domain-Namen nicht-englische Zeichen enthalten können. Aber die meiste Software hält sich immer noch an die 37 Zeichen, die westliche Programmierer gewohnt sind. Die Unterstützung von internationalisierten Domains öffnet eine ganze Dose von Würmern, wie die Nicht-ASCII-Zeichen kodiert werden sollten. Wenn Sie also eine der Regexe auf dieser Seite verwenden, hat jeder mit einer @ทีเอชนิค.ไทย-Adresse Pech gehabt. Aber vielleicht ist es bezeichnend, dass http://ทีเอชนิค.ไทย einfach auf http://thnic.co.th weiterleitet, obwohl sie im Geschäft mit dem Verkauf von .ไทย-Domains sind.
Die Schlussfolgerung ist, dass man bei der Entscheidung, welchen regulären Ausdruck man verwenden soll, egal ob man versucht, eine E-Mail-Adresse oder etwas anderes, das vage definiert ist, abzugleichen, damit beginnen muss, alle Kompromisse zu berücksichtigen. Wie schlecht ist es, auf etwas zu passen, das nicht gültig ist? Wie schlimm ist es, wenn etwas nicht übereinstimmt, das gültig ist? Wie komplex darf Ihr regulärer Ausdruck sein? Wie teuer wäre es, wenn Sie den regulären Ausdruck später ändern müssten, weil er sich als zu breit oder zu eng herausstellt? Unterschiedliche Antworten auf diese Fragen werden einen anderen regulären Ausdruck als Lösung erfordern. Meine E-Mail-Regex macht, was ich will, aber sie macht vielleicht nicht, was Sie wollen.
Regexe senden keine E-Mails
Übertreiben Sie es nicht mit dem Versuch, ungültige E-Mail-Adressen mit Ihrem regulären Ausdruck zu eliminieren. Der Grund dafür ist, dass Sie nicht wirklich wissen, ob eine Adresse gültig ist, bis Sie versuchen, eine E-Mail an sie zu senden. Und selbst das könnte nicht genug sein. Selbst wenn die E-Mail in einem Postfach ankommt, heißt das noch lange nicht, dass jemand dieses Postfach liest. Wenn Sie wirklich sicher sein wollen, dass eine E-Mail-Adresse gültig ist, müssen Sie eine E-Mail an sie senden, die einen Code oder Link enthält, mit dem der Empfänger einen zweiten Authentifizierungsschritt durchführen kann. Und wenn Sie das tun, dann macht es wenig Sinn, eine Regex zu verwenden, die möglicherweise gültige E-Mail-Adressen zurückweist.
Das gleiche Prinzip gilt in vielen Situationen. Wenn Sie versuchen, ein gültiges Datum zu finden, ist es oft einfacher, ein wenig Arithmetik zu verwenden, um auf Schaltjahre zu prüfen, als zu versuchen, dies in einer Regex zu tun. Verwenden Sie einen regulären Ausdruck, um potenzielle Übereinstimmungen zu finden, oder prüfen Sie, ob die Eingabe die richtige Syntax verwendet, und führen Sie die eigentliche Überprüfung an den potenziellen Übereinstimmungen durch, die der reguläre Ausdruck zurückgibt. Reguläre Ausdrücke sind ein mächtiges Werkzeug, aber sie sind weit davon entfernt, ein Allheilmittel zu sein.
Der offizielle Standard: RFC 5322
Vielleicht fragen Sie sich, warum es keinen „offiziellen“, narrensicheren regulären Ausdruck zum Abgleich von E-Mail-Adressen gibt. Nun, es gibt eine offizielle Definition, aber sie ist kaum narrensicher.
Der offizielle Standard ist als RFC 5322 bekannt. Er beschreibt die Syntax, an die sich gültige E-Mail-Adressen halten müssen. Sie können (sollten aber nicht – lesen Sie weiter) sie mit dem folgenden regulären Ausdruck implementieren. RFC 5322 lässt den Teil mit den Domänennamen offen für implementierungsspezifische Entscheidungen, die im heutigen Internet nicht funktionieren werden. Der Regex implementiert die „bevorzugte“ Syntax aus RFC 1035, die eine der Empfehlungen in RFC 5322 ist:
A(?:+(?:\.+)*
| „(?:
| \\)*“)
@ (?:(?\.)+(?:*)?
| \|2|??)\.){3}
(?:25|2|??|*:
(?:
| \\)+)
\)\z
Diese Regex hat zwei Teile: den Teil vor dem @ und den Teil nach dem @. Für den Teil vor dem @ gibt es zwei Alternativen. Die erste Alternative erlaubt es, dass er aus einer Reihe von Buchstaben, Ziffern und bestimmten Symbolen besteht, einschließlich eines oder mehrerer Punkte. Die Punkte dürfen jedoch nicht fortlaufend oder am Anfang oder Ende der E-Mail-Adresse erscheinen. Die andere Alternative verlangt, dass der Teil vor dem @ in doppelte Anführungszeichen eingeschlossen wird, wobei eine beliebige Folge von ASCII-Zeichen zwischen den Anführungszeichen erlaubt ist. Leerzeichen, doppelte Anführungszeichen und Backslashes müssen mit Backslashes escaped werden.
Der Teil nach dem @ hat ebenfalls zwei Alternativen. Er kann entweder ein vollqualifizierter Domainname sein (z. B. regular-expressions.info), oder er kann eine literale Internetadresse zwischen eckigen Klammern sein. Die wörtliche Internetadresse kann entweder eine IP-Adresse oder eine domänenspezifische Routing-Adresse sein.
Der Grund, warum Sie diese Regex nicht verwenden sollten, ist, dass sie zu breit ist. Ihre Anwendung ist möglicherweise nicht in der Lage, alle E-Mail-Adressen zu verarbeiten, die diese Regex zulässt. Domänenspezifische Routing-Adressen können nicht druckbare ASCII-Steuerzeichen enthalten, was zu Problemen führen kann, wenn Ihre Anwendung Adressen anzeigen muss. Nicht alle Anwendungen unterstützen die Syntax für den lokalen Teil mit doppelten Anführungszeichen oder eckigen Klammern. Tatsächlich markiert RFC 5322 selbst die Notation mit eckigen Klammern als veraltet.
Wir erhalten eine praktischere Implementierung von RFC 5322, wenn wir IP-Adressen, domänenspezifische Adressen, die Syntax mit doppelten Anführungszeichen und eckigen Klammern weglassen. Sie wird immer noch mit 99,99 % aller heute tatsächlich verwendeten E-Mail-Adressen übereinstimmen.
A+(?:\.+)*@
(?:(?:*)?\.)+(?:*)?\z
Keine dieser Regexen erzwingt Längenbeschränkungen für die gesamte E-Mail-Adresse oder den lokalen Teil oder die Domainnamen. RFC 5322 spezifiziert keine Längenbeschränkungen. Diese ergeben sich aus Beschränkungen in anderen Protokollen wie dem SMTP-Protokoll für den eigentlichen E-Mail-Versand. RFC 1035 gibt zwar an, dass Domains maximal 63 Zeichen lang sein dürfen, aber das ist nicht in der Syntaxspezifikation enthalten. Der Grund dafür ist, dass eine echte reguläre Sprache nicht gleichzeitig eine Längenbegrenzung erzwingen und aufeinanderfolgende Bindestriche verbieten kann. Aber moderne Regex-Varianten sind nicht wirklich regulär, also können wir Längenlimit-Prüfungen mit Lookahead hinzufügen, wie wir es zuvor getan haben:
A(?={6,254}\z)
(?={1,64}@)
+(?:\.+)*
@ (?:(?={1,63}\.)(?:*)?\.)+
(?={1,63}\z)(?:*)?\z
Selbst wenn Sie sich an die offiziellen Standards halten, gibt es also immer noch Kompromisse, die Sie eingehen müssen. Kopieren Sie nicht blindlings reguläre Ausdrücke aus Online-Bibliotheken oder Diskussionsforen. Testen Sie sie immer an Ihren eigenen Daten und mit Ihren eigenen Anwendungen.
Spenden Sie
Hat Ihnen diese Website gerade einen Gang zum Buchladen erspart? Unterstützen Sie diese Seite mit einer Spende, und Sie erhalten ein Leben lang werbefreien Zugang zu dieser Seite!