Niedawno pisał odpowiedź na to pytanie na temat kodów pocztowych Wielkiej Brytanii na języku R . Odkryłem, że wzorzec wyrażenia regularnego rządu brytyjskiego jest nieprawidłowy i nie działa prawidłowo zweryfikować niektóre kody pocztowe. Niestety wiele odpowiedzi tutaj opiera się na tym niepoprawnym wzorze.
Poniżej opiszę niektóre z tych problemów i przedstawię poprawione wyrażenie regularne, które faktycznie działa.
Uwaga
Moja odpowiedź (i ogólnie wyrażenia regularne):
- Sprawdza tylko formaty kodów pocztowych .
- Nie zapewnia legalności istnienia kodu pocztowego .
- W tym celu użyj odpowiedniego interfejsu API! Zobacz odpowiedź Bena, aby uzyskać więcej informacji.
Jeśli nie zależy ci na złym wyrażeniu regularnym i po prostu chcesz przejść do odpowiedzi, przewiń w dół do odpowiedzi sekcji .
The Bad Regex
Nie należy używać wyrażeń regularnych w tej sekcji.
Jest to błąd polegający na tym, że rząd Wielkiej Brytanii dostarczył programistom (nie jestem pewien, jak długo będzie działał ten link, ale można to zobaczyć w dokumentacji Bulk Data Transfer ):
^([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})$
Problemy
Problem 1 - Kopiuj / Wklej
Zobacz regex w użyciu tutaj .
Jak zapewne wielu programistów, kopiuje / wkleja kod (szczególnie wyrażenia regularne) i wkleja, oczekując, że zadziałają. Chociaż jest to świetne w teorii, nie udaje się w tym konkretnym przypadku, ponieważ kopiowanie / wklejanie z tego dokumentu faktycznie zmienia jeden ze znaków (spację) na znak nowej linii, jak pokazano poniżej:
^([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})$
Pierwszą rzeczą, którą zrobią większość programistów, jest po prostu usunięcie nowej linii bez zastanowienia się. Teraz wyrażenie regularne nie będzie pasowało do kodów pocztowych ze spacjami w nich (innymi niżGIR 0AA
kod pocztowy).
Aby rozwiązać ten problem, znak nowej linii należy zastąpić znakiem spacji:
^([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 - Granice
Zobacz regex w użyciu tutaj .
^([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})$
^^ ^ ^ ^^
Wyrażenie kodu pocztowego nieprawidłowo zakotwicza wyrażenie regularne. Każdy, kto używa tego wyrażenia regularnego do sprawdzania kodów pocztowych, może być zaskoczony, jeśli wartość taka jakfooA11 1AA
. Jest tak, ponieważ zakotwiczili początek pierwszej opcji i koniec drugiej opcji (niezależnie od siebie), jak wskazano w wyrażeniu regularnym powyżej.
Oznacza to, że ^
(zapewnia pozycję na początku wiersza) działa tylko w pierwszej opcji ([Gg][Ii][Rr] 0[Aa]{2})
, więc druga opcja zweryfikuje wszystkie ciągi, które się kończą się kodem pocztowym (niezależnie od tego, co nastąpi wcześniej).
Podobnie pierwsza opcja nie jest zakotwiczona na końcu linii $
, więc GIR 0AAfoo
jest również akceptowana.
^([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})$
Aby rozwiązać ten problem, obie opcje należy zapakować w inną grupę (lub grupę nieprzechwycącą), a kotwice umieścić wokół tego:
^(([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 - Niewłaściwy zestaw znaków
Zobacz regex w użyciu tutaj .
^([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})$
^^
Wyrażenie regularne brakuje -
tutaj, aby wskazać zakres znaków. W obecnej postaci, jeśli kod pocztowy ma format ANA NAA
(gdzie A
reprezentuje literę i N
cyfrę) i zaczyna się od czegoś innego niż A
lubZ
, nie powiedzie się.
Oznacza to, że będzie on pasował A1A 1AA
i Z1A 1AA
, ale nie B1A 1AA
.
Aby rozwiązać ten problem, znak -
należy umieścić między A
oraz Z
w odpowiednim zestawie znaków:
^([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 - Niepoprawny opcjonalny zestaw znaków
Zobacz regex w użyciu tutaj .
^([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})$
^
Przysięgam, że nawet nie przetestowali tego przed opublikowaniem go w Internecie. Zrobili opcjonalny niewłaściwy zestaw znaków. Dokonali [0-9]
opcji w czwartym podwariacie opcji 2 (grupa 9). Dzięki temu wyrażenie regularne dopasowuje niepoprawnie sformatowane kody pocztowe, takie jakAAA 1AA
.
Aby rozwiązać ten problem, ustaw opcjonalną następną klasę znaków (a następnie [0-9]
dopasuj zestaw dokładnie raz):
^([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 - Wydajność
Wydajność tego wyrażenia regularnego jest bardzo niska. Po pierwsze, umieścili najmniej prawdopodobną opcję dopasowania GIR 0AA
na początku. Ilu użytkowników prawdopodobnie będzie miało ten kod pocztowy w porównaniu do dowolnego innego kodu pocztowego; prawdopodobnie nigdy? Oznacza to, że przy każdym użyciu wyrażenia regularnego musi on wyczerpać tę opcję przed przejściem do następnej opcji. Aby zobaczyć, jak wpływa to na wydajność, sprawdź liczbę kroków, które wykonał pierwotny regex (35) w stosunku do tego samego regex po odwróceniu opcji (22).
Drugi problem z wydajnością wynika ze struktury całego wyrażenia regularnego. Nie ma sensu cofanie się po każdej opcji, jeśli jedna zawiedzie. Strukturę obecnego wyrażenia regularnego można znacznie uprościć. Podaję poprawkę w sekcji Odpowiedź .
Problem 6 - Przestrzenie
Zobacz regex w użyciu tutaj
Nie może to być uważane za problem , ale budzi zaniepokojenie większości programistów. Spacje w wyrażeniu regularnym nie są opcjonalne, co oznacza, że użytkownicy wprowadzający swoje kody pocztowe muszą umieścić spację w kodzie pocztowym. Jest to łatwa poprawka, po prostu dodając ?
spacje, aby uczynić je opcjonalnymi. Aby uzyskać poprawkę, zobacz sekcję Odpowiedź .
Odpowiedź
1. Ustalenie Regeksu rządu Wielkiej Brytanii
Naprawienie wszystkich problemów opisanych w sekcji Problemy i uproszczenie wzoru daje następujący, krótszy i bardziej zwięzły wzór. Możemy również usunąć większość grup, ponieważ weryfikujemy kod pocztowy jako całość (nie poszczególne części):
Zobacz regex w użyciu tutaj
^([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})$
Można to dodatkowo skrócić, usuwając wszystkie zakresy z jednego ze znaków (wielkie lub małe) i używając flagi bez rozróżniania wielkości liter. Uwaga : niektóre języki nie mają takiego, więc użyj dłuższego z powyższych. Każdy język implementuje flagę rozróżniania wielkości liter inaczej.
Zobacz regex w użyciu tutaj .
^([A-Z][A-HJ-Y]?[0-9][A-Z0-9]? ?[0-9][A-Z]{2}|GIR ?0A{2})$
Krótszy ponownie wymianie [0-9]
z \d
(jeśli silnik regex je obsługuje):
Zobacz regex w użyciu tutaj .
^([A-Z][A-HJ-Y]?\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
2. Uproszczone wzory
Bez konieczności podawania określonych znaków alfabetycznych można użyć następujących (pamiętaj o uproszczeniach z 1. Ustalono również tutaj Regex Rządu Zjednoczonego Królestwa ):
Zobacz regex w użyciu tutaj .
^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
Co więcej, jeśli nie zależy ci na specjalnym przypadku GIR 0AA
:
^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$
3. Skomplikowane wzory
Nie sugeruję nadmiernej weryfikacji kodu pocztowego, ponieważ nowe Obszary, Dzielnice i Podrejgi mogą pojawić się w dowolnym momencie. To, co zasugeruję potencjalnie do zrobienia, to dodane wsparcie dla przypadków skrajnych. Istnieją specjalne przypadki, które opisano w tym artykule w Wikipedii .
Oto złożone wyrażenia regularne, które zawierają podrozdziały 3. (3.1, 3.2, 3.3).
W odniesieniu do wzorców w 1. Naprawianie Regeksu rządu Wielkiej Brytanii :
Zobacz regex w użyciu tutaj
^(([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)$
I w związku z 2. Uproszczonymi wzorami :
Zobacz regex w użyciu tutaj
^(([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 Brytyjskie terytoria zamorskie
Artykuł w Wikipedii stwierdza obecnie (niektóre formaty nieco uproszczone):
AI-1111
: Anguila
ASCN 1ZZ
: Wyspa Wniebowstąpienia
STHL 1ZZ
: Święta Helena
TDCU 1ZZ
: Tristan da Cunha
BBND 1ZZ
: Brytyjskie Terytorium Oceanu Indyjskiego
BIQQ 1ZZ
: Brytyjskie Terytorium Antarktyczne
FIQQ 1ZZ
: Falklandy
GX11 1ZZ
: Gibraltar
PCRN 1ZZ
: Wyspy Pitcairn
SIQQ 1ZZ
: Georgia Południowa i Sandwich Południowy
TKCA 1ZZ
: Wyspy Turks i Caicos
BFPO 11
: Akrotiri i Dhekelia
ZZ 11
I GE CX
: Bermudy (zgodnie z tym dokumentem )
KY1-1111
: Kajmany (zgodnie z tym dokumentem )
VG1111
: Brytyjskie Wyspy Dziewicze (zgodnie z tym dokumentem )
MSR 1111
: Montserrat (zgodnie z tym dokumentem )
Kompleksowy regex pasujący tylko do brytyjskich terytoriów zamorskich może wyglądać następująco:
Zobacz regex w użyciu tutaj .
^((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 Urząd pocztowy sił brytyjskich
Chociaż zostały ostatnio zmienione, aby lepiej dostosować się do brytyjskiego systemu kodów pocztowych do BF#
(gdzie #
reprezentuje liczbę), są uważane za opcjonalne alternatywne kody pocztowe . Te kody pocztowe mają (ed) format BFPO
, a następnie 1-4 cyfry:
Zobacz regex w użyciu tutaj
^BFPO ?\d{1,4}$
3.3 Święty Mikołaj?
Mikołaj ma inny szczególny przypadek (jak wspomniano w innych odpowiedziach): SAN TA1
jest to prawidłowy kod pocztowy. Wyrażenie regularne tego jest bardzo proste:
^SAN ?TA1$