Ich habe kürzlich eine Antwort auf diese Frage auf britischen Postleitzahlen für die R-Sprache veröffentlicht . Ich habe festgestellt, dass das Regex-Muster der britischen Regierung falsch ist und nicht richtig funktioniert einige Postleitzahlen validiert werden kann. Leider basieren viele der Antworten hier auf diesem falschen Muster.
Ich werde im Folgenden einige dieser Probleme skizzieren und einen überarbeiteten regulären Ausdruck bereitstellen, der tatsächlich funktioniert.
Hinweis
Meine Antwort (und reguläre Ausdrücke im Allgemeinen):
- Nur validiert Postleitzahlenformate .
- Stellt nicht sicher, dass eine Postleitzahl rechtmäßig vorhanden ist .
- Verwenden Sie dazu eine entsprechende API! Weitere Informationen finden Sie in Bens Antwort .
Wenn Sie sich nicht für den schlechten regulären Ausdruck interessieren und nur zur Antwort springen möchten, scrollen Sie nach unten zum Abschnitt Antwort .
Der schlechte Regex
Die regulären Ausdrücke in diesem Abschnitt sollten nicht verwendet werden.
Dies ist die fehlgeschlagene Regex, die die britische Regierung Entwicklern zur Verfügung gestellt hat (nicht sicher, wie lange dieser Link bestehen wird, aber Sie können ihn in ihrer Dokumentation zur Massendatenübertragung sehen ):
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$
Probleme
Problem 1 - Kopieren / Einfügen
Siehe Regex, der hier verwendet wird .
Wie viele Entwickler wahrscheinlich, kopieren / fügen sie Code (insbesondere reguläre Ausdrücke) ein und fügen sie ein, damit sie funktionieren. Obwohl dies theoretisch großartig ist, schlägt es in diesem speziellen Fall fehl, weil das Kopieren / Einfügen aus diesem Dokument tatsächlich eines der Zeichen (ein Leerzeichen) in ein Zeilenumbruchzeichen ändert, wie unten gezeigt:
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))
[0-9][A-Za-z]{2})$
Das erste, was die meisten Entwickler tun, ist, die neue Zeile zu löschen, ohne darüber nachzudenken. Jetzt ordnet der reguläre Ausdruck Postleitzahlen nicht mehr Leerzeichen zu (außer demGIR 0AA
Postleitzahl).
Um dieses Problem zu beheben, sollte das Zeilenumbruchzeichen durch das Leerzeichen ersetzt werden:
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^
Problem 2 - Grenzen
Siehe Regex, der hier verwendet wird .
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^^ ^ ^ ^^
Der Postleitzahl-Regex verankert den Regex nicht ordnungsgemäß. Jeder, der diese Regex verwendet, um Postleitzahlen zu validieren, könnte überrascht sein, wenn ein Wert wie fooA11 1AA
durchkommt. Das liegt daran, dass sie den Anfang der ersten Option und das Ende der zweiten Option (unabhängig voneinander) verankert haben, wie im obigen regulären Ausdruck ausgeführt.
Dies bedeutet, dass ^
(Position am Zeilenanfang bestätigt) nur für die erste Option funktioniert ([Gg][Ii][Rr] 0[Aa]{2})
, sodass die zweite Option alle Zeichenfolgen überprüft, die enden mit einer Postleitzahl (unabhängig davon, was vorher kommt).
In ähnlicher Weise ist die erste Option nicht bis zum Ende der Zeile verankert $
, so GIR 0AAfoo
auch akzeptiert wird.
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$
Um dieses Problem zu beheben, sollten beide Optionen in eine andere Gruppe (oder eine nicht erfassende Gruppe) eingeschlossen und die Anker um diese herum platziert werden:
^(([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2}))$
^^ ^^
Problem 3 - Falscher Zeichensatz
Siehe Regex, der hier verwendet wird .
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^^
In der Regex fehlt -
hier ein Zeichen für eine Reihe von Zeichen. So wie es aussieht, wenn eine Postleitzahl das Format hat ANA NAA
(wobei A
ein Buchstabe und N
eine Zahl steht) und mit etwas anderem als A
oder beginntZ
sie fehl.
Das heißt, es wird passen A1A 1AA
und Z1A 1AA
, aber nicht B1A 1AA
.
Um dieses Problem zu beheben, sollte das Zeichen -
zwischen A
und Z
im jeweiligen Zeichensatz platziert werden:
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^
Problem 4 - Falscher optionaler Zeichensatz
Siehe Regex, der hier verwendet wird .
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^
Ich schwöre, sie haben dieses Ding nicht einmal getestet, bevor sie es im Internet veröffentlicht haben. Sie haben den falschen Zeichensatz optional gemacht. Sie machten [0-9]
Option in der vierten Unteroption von Option 2 (Gruppe 9). Dadurch kann der Regex mit falsch formatierten Postleitzahlen wie übereinstimmen AAA 1AA
.
Um dieses Problem zu beheben, machen Sie stattdessen die nächste Zeichenklasse optional (und lassen Sie den Satz anschließend [0-9]
genau einmal übereinstimmen):
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?)))) [0-9][A-Za-z]{2})$
^
Problem 5 - Leistung
Die Leistung dieser Regex ist extrem schlecht. Zunächst platzierten sie die am wenigsten wahrscheinliche Musteroption, GIR 0AA
die zu Beginn übereinstimmt . Wie viele Benutzer werden diese Postleitzahl wahrscheinlich im Vergleich zu einer anderen Postleitzahl haben? wahrscheinlich nie? Dies bedeutet, dass bei jeder Verwendung des regulären Ausdrucks diese Option zuerst ausgeschöpft werden muss, bevor mit der nächsten Option fortgefahren werden kann. Um zu sehen, wie sich die Leistung auf die Leistung auswirkt, überprüfen Sie die Anzahl der Schritte, die der ursprüngliche reguläre Ausdruck (35) mit demselben regulären Ausdruck ausgeführt hat, nachdem Sie die Optionen umgedreht haben (22).
Das zweite Problem mit der Leistung ist auf die Struktur des gesamten regulären Ausdrucks zurückzuführen. Es macht keinen Sinn, jede Option zurückzuverfolgen, wenn eine fehlschlägt. Die Struktur des aktuellen regulären Ausdrucks kann erheblich vereinfacht werden. Ich gebe eine Lösung dafür im Abschnitt Antwort .
Problem 6 - Leerzeichen
Siehe Regex, der hier verwendet wird
Dies kann an sich nicht als Problem angesehen werden , gibt jedoch den meisten Entwicklern Anlass zur Sorge. Die Leerzeichen in der Regex sind nicht optional. Dies bedeutet, dass die Benutzer, die ihre Postleitzahlen eingeben, ein Leerzeichen in die Postleitzahl einfügen müssen. Dies ist eine einfache Lösung, indem Sie einfach ?
nach den Leerzeichen hinzufügen , um sie optional zu machen. Eine Lösung finden Sie im Abschnitt Antwort .
Antworten
1. Festsetzung des Regex der britischen Regierung
Wenn Sie alle im Abschnitt Probleme beschriebenen Probleme beheben und das Muster vereinfachen, erhalten Sie das folgende, kürzere und präzisere Muster. Wir können auch die meisten Gruppen entfernen, da wir die Postleitzahl als Ganzes validieren (nicht einzelne Teile):
Siehe Regex, der hier verwendet wird
^([A-Za-z][A-Ha-hJ-Yj-y]?[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}|[Gg][Ii][Rr] ?0[Aa]{2})$
Dies kann weiter verkürzt werden, indem alle Bereiche aus einem der Fälle (Groß- oder Kleinschreibung) entfernt und ein Flag verwendet werden, bei dem die Groß- und Kleinschreibung nicht berücksichtigt wird. Hinweis : Einige Sprachen haben keine, verwenden Sie also die längere oben. Jede Sprache implementiert das Flag für Groß- und Kleinschreibung anders.
Siehe Regex, der hier verwendet wird .
^([A-Z][A-HJ-Y]?[0-9][A-Z0-9]? ?[0-9][A-Z]{2}|GIR ?0A{2})$
Kürzere wieder ersetzt [0-9]
mit \d
(wenn Ihre Regex - Engine unterstützt):
Siehe Regex, der hier verwendet wird .
^([A-Z][A-HJ-Y]?\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
2. Vereinfachte Muster
Ohne bestimmte alphabetische Zeichen zu gewährleisten, kann Folgendes verwendet werden (beachten Sie die Vereinfachungen ab 1. Die Korrektur des Regex der britischen Regierung wurde auch hier angewendet):
Siehe Regex, der hier verwendet wird .
^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
Und noch weiter, wenn Sie sich nicht für den Sonderfall interessieren GIR 0AA
:
^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$
3. Komplizierte Muster
Ich würde nicht empfehlen, eine Postleitzahl zu stark zu überprüfen, da zu jedem Zeitpunkt neue Gebiete, Bezirke und Unterbezirke erscheinen können. Was ich möglicherweise vorschlagen werde , ist die zusätzliche Unterstützung für Randfälle. Einige Sonderfälle existieren und werden in diesem Wikipedia-Artikel beschrieben .
Hier sind komplexe reguläre Ausdrücke, die die Unterabschnitte von 3. (3.1, 3.2, 3.3) enthalten.
In Bezug auf die Muster in 1. Festsetzung des Regex der britischen Regierung :
Siehe Regex, der hier verwendet wird
^(([A-Z][A-HJ-Y]?\d[A-Z\d]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?\d[A-Z]{2}|BFPO ?\d{1,4}|(KY\d|MSR|VG|AI)[ -]?\d{4}|[A-Z]{2} ?\d{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$
Und in Bezug auf 2. Vereinfachte Muster :
Siehe Regex, der hier verwendet wird
^(([A-Z]{1,2}\d[A-Z\d]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?\d[A-Z]{2}|BFPO ?\d{1,4}|(KY\d|MSR|VG|AI)[ -]?\d{4}|[A-Z]{2} ?\d{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$
3.1 Britische Überseegebiete
Im Wikipedia-Artikel heißt es derzeit (einige Formate leicht vereinfacht):
AI-1111
: Anguila
ASCN 1ZZ
: Ascension Island
STHL 1ZZ
: Heilige Helena
TDCU 1ZZ
: Tristan da Cunha
BBND 1ZZ
: Britisches Territorium des Indischen Ozeans
BIQQ 1ZZ
: Britisches Antarktisgebiet
FIQQ 1ZZ
: Falkland Inseln
GX11 1ZZ
: Gibraltar
PCRN 1ZZ
: Pitcairninseln
SIQQ 1ZZ
: Süd-Georgien und die südlichen Sandwich-Inseln
TKCA 1ZZ
: Turks- und Caicosinseln
BFPO 11
: Akrotiri und Dhekelia
ZZ 11
& GE CX
: Bermuda (laut diesem Dokument )
KY1-1111
: Cayman Islands (gemäß diesem Dokument )
VG1111
: Britische Jungferninseln (gemäß diesem Dokument )
MSR 1111
: Montserrat (gemäß diesem Dokument )
Ein umfassender Regex, der nur den britischen Überseegebieten entspricht, könnte folgendermaßen aussehen:
Siehe Regex, der hier verwendet wird .
^((ASCN|STHL|TDCU|BBND|[BFS]IQQ|GX\d{2}|PCRN|TKCA) ?\d[A-Z]{2}|(KY\d|MSR|VG|AI)[ -]?\d{4}|(BFPO|[A-Z]{2}) ?\d{2}|GE ?CX)$
3.2 Post der britischen Streitkräfte
Obwohl sie kürzlich geändert wurden, um sie besser an das britische Postleitzahlensystem anzupassen BF#
(wobei #
eine Zahl steht), gelten sie als optionale alternative Postleitzahlen . Diese Postleitzahlen folgen dem Format von BFPO
, gefolgt von 1-4 Ziffern:
Siehe Regex, der hier verwendet wird
^BFPO ?\d{1,4}$
3.3 Santa?
Es gibt noch einen weiteren Sonderfall mit dem Weihnachtsmann (wie in anderen Antworten erwähnt): SAN TA1
ist eine gültige Postleitzahl. Eine Regex dafür ist sehr einfach:
^SAN ?TA1$