Gần đây tôi đã đăng tải một câu trả lời cho câu hỏi này trên postcodes Anh cho ngôn ngữ R . Tôi phát hiện ra rằng mẫu regex của Chính phủ Vương quốc Anh là không chính xác và không xác nhận đúng một số mã bưu điện. Thật không may, nhiều câu trả lời ở đây dựa trên mẫu không chính xác này.
Tôi sẽ phác thảo một số vấn đề dưới đây và cung cấp một biểu thức chính quy được sửa đổi thực sự hoạt động.
Ghi chú
Câu trả lời của tôi (và các biểu thức chính quy nói chung):
- Chỉ xác nhận các định dạng mã bưu điện .
- Không đảm bảo rằng một mã bưu chính tồn tại hợp pháp .
Nếu bạn không quan tâm đến regex xấu và chỉ muốn bỏ qua câu trả lời, hãy cuộn xuống phần Trả lời .
Regex xấu
Các biểu thức chính quy trong phần này không nên được sử dụng.
Đây là regex thất bại mà chính phủ Anh đã cung cấp cho các nhà phát triển (không chắc liên kết này sẽ kéo dài bao lâu, nhưng bạn có thể thấy nó trong tài liệu Chuyển dữ liệu hàng loạt của họ ):
^([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})$
Các vấn đề
Vấn đề 1 - Sao chép / Dán
Xem regex đang sử dụng ở đây .
Như nhiều nhà phát triển có thể làm, họ sao chép / dán mã (đặc biệt là các biểu thức thông thường) và dán chúng để mong chúng hoạt động. Mặc dù điều này là lý thuyết tuyệt vời, nhưng nó thất bại trong trường hợp cụ thể này vì sao chép / dán từ tài liệu này thực sự thay đổi một trong các ký tự (khoảng trắng) thành ký tự dòng mới như dưới đây:
^([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})$
Điều đầu tiên mà hầu hết các nhà phát triển sẽ làm chỉ là xóa dòng mới mà không cần suy nghĩ kỹ. Bây giờ regex sẽ không khớp mã bưu điện với khoảng trắng trong chúng (trừ GIR 0AA
mã bưu điện).
Để khắc phục sự cố này, nên thay thế ký tự dòng mới bằng ký tự khoảng trắng:
^([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})$
^
Vấn đề 2 - Ranh giới
Xem regex đang sử dụng ở đây .
^([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})$
^^ ^ ^ ^^
Mã bưu chính regex không đúng cách neo regex. Bất cứ ai sử dụng regex này để xác nhận mã bưu điện có thể ngạc nhiên nếu một giá trị như fooA11 1AA
được thông qua. Đó là bởi vì họ đã thả neo bắt đầu tùy chọn đầu tiên và kết thúc tùy chọn thứ hai (độc lập với nhau), như được chỉ ra trong biểu thức chính thức ở trên.
Điều này có nghĩa là ^
(xác nhận vị trí ở đầu dòng) chỉ hoạt động trên tùy chọn đầu tiên ([Gg][Ii][Rr] 0[Aa]{2})
, vì vậy tùy chọn thứ hai sẽ xác thực bất kỳ chuỗi nào kết thúc bằng mã bưu điện (bất kể trước đó là gì).
Tương tự, tùy chọn đầu tiên không được neo vào cuối dòng $
, do đó GIR 0AAfoo
cũng được chấp nhận.
^([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})$
Để khắc phục vấn đề này, cả hai tùy chọn nên được bọc trong một nhóm khác (hoặc nhóm không bắt giữ) và các neo được đặt xung quanh đó:
^(([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}))$
^^ ^^
Vấn đề 3 - Bộ ký tự không đúng
Xem regex đang sử dụng ở đây .
^([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})$
^^
Regex bị thiếu -
ở đây để chỉ ra một loạt các ký tự. Vì nó đứng, nếu một mã bưu điện có định dạng ANA NAA
(trong đó A
đại diện cho một chữ cái và N
đại diện cho một số), và nó bắt đầu bằng bất cứ điều gì khác A
hoặc Z
, nó sẽ thất bại.
Điều đó có nghĩa là nó sẽ phù hợp A1A 1AA
và Z1A 1AA
, nhưng không B1A 1AA
.
Để khắc phục sự cố này, ký tự -
phải được đặt giữa A
và Z
trong bộ ký tự tương ứng:
^([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})$
^
Vấn đề 4 - Bộ ký tự tùy chọn sai
Xem regex đang sử dụng ở đây .
^([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})$
^
Tôi thề họ thậm chí không kiểm tra điều này trước khi công khai nó trên web. Họ đã đặt sai ký tự tùy chọn. Họ đã thực hiện [0-9]
tùy chọn trong tùy chọn phụ thứ tư của tùy chọn 2 (nhóm 9). Điều này cho phép regex khớp với các mã bưu điện được định dạng không chính xác như thế nào AAA 1AA
.
Để khắc phục sự cố này, thay vào đó, hãy tạo lớp nhân vật tiếp theo tùy chọn (và sau đó làm cho tập [0-9]
hợp khớp chính xác một lần):
^([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})$
^
Vấn đề 5 - Hiệu suất
Hiệu suất trên regex này là cực kỳ kém. Trước hết, họ đặt tùy chọn mẫu ít có khả năng khớp nhất GIR 0AA
lúc ban đầu. Có bao nhiêu người dùng có thể sẽ có mã bưu điện này so với bất kỳ mã bưu điện nào khác; Có lẽ không bao giờ? Điều này có nghĩa là mỗi khi regex được sử dụng, nó phải sử dụng hết tùy chọn này trước khi tiếp tục tùy chọn tiếp theo. Để xem hiệu suất bị ảnh hưởng như thế nào, hãy kiểm tra số bước mà regex ban đầu đã thực hiện (35) so với regex tương tự sau khi đã lật các tùy chọn (22).
Vấn đề thứ hai với hiệu suất là do cách cấu trúc toàn bộ regex. Không có điểm quay lại qua từng tùy chọn nếu một thất bại. Cách thức cấu trúc regex hiện tại có thể được đơn giản hóa rất nhiều. Tôi cung cấp một sửa chữa cho điều này trong phần Trả lời .
Bài 6 - Dấu cách
Xem regex đang sử dụng tại đây
Điều này có thể không được coi là một vấn đề , nhưng sẽ gây lo ngại cho hầu hết các nhà phát triển. Các khoảng trắng trong regex không phải là tùy chọn, có nghĩa là người dùng nhập mã bưu điện của họ phải đặt một khoảng trắng trong mã bưu điện. Đây là một sửa chữa dễ dàng bằng cách thêm vào ?
sau khoảng trắng để hiển thị chúng tùy chọn. Xem Trả lời để khắc phục.
Câu trả lời
1. Sửa Regex Chính phủ Vương quốc Anh
Sửa chữa tất cả các vấn đề được nêu trong vấn đề phần và đơn giản hóa mô hình mang lại những điều sau đây, ngắn hơn, hoa văn súc tích hơn. Chúng tôi cũng có thể xóa hầu hết các nhóm vì chúng tôi xác nhận toàn bộ mã bưu điện (không phải từng phần riêng lẻ):
Xem regex đang sử dụng tại đây
^([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})$
Điều này có thể được rút ngắn hơn nữa bằng cách loại bỏ tất cả các phạm vi khỏi một trong các trường hợp (chữ hoa hoặc chữ thường) và sử dụng cờ không phân biệt chữ hoa chữ thường. Ghi chú : Một số ngôn ngữ không có ngôn ngữ, vì vậy hãy sử dụng ngôn ngữ dài hơn ở trên. Mỗi ngôn ngữ thực hiện cờ không phân biệt chữ hoa chữ thường.
Xem regex đang sử dụng ở đây .
^([A-Z][A-HJ-Y]?[0-9][A-Z0-9]? ?[0-9][A-Z]{2}|GIR ?0A{2})$
Ngắn hơn một lần nữa thay thế [0-9]
bằng \d
(nếu công cụ regex của bạn hỗ trợ nó):
Xem regex đang sử dụng ở đây .
^([A-Z][A-HJ-Y]?\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
2. Các mẫu đơn giản hóa
Không đảm bảo các ký tự chữ cái cụ thể, có thể sử dụng các ký tự sau (ghi nhớ các đơn giản hóa từ 1. Sửa lỗi Regex của Chính phủ Vương quốc Anh cũng đã được áp dụng tại đây):
Xem regex đang sử dụng ở đây .
^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
Và thậm chí xa hơn nếu bạn không quan tâm đến trường hợp đặc biệt GIR 0AA
:
^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$
3. Các mẫu phức tạp
Tôi sẽ không đề xuất xác minh quá mức mã bưu điện vì Khu vực, Quận và Tiểu huyện mới có thể xuất hiện bất cứ lúc nào. Những gì tôi sẽ đề xuất có khả năng làm, được thêm hỗ trợ cho các trường hợp cạnh. Một số trường hợp đặc biệt tồn tại và được nêu trong bài viết Wikipedia này .
Dưới đây là các biểu thức phức tạp bao gồm các phần phụ của 3. (3.1, 3.2, 3.3).
Liên quan đến các mẫu trong 1. Sửa Chính sách Chính phủ Vương quốc Anh :
Xem regex đang sử dụng tại đây
^(([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)$
Và liên quan đến 2. Các mẫu đơn giản hóa :
Xem regex đang sử dụng tại đây
^(([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 Lãnh thổ hải ngoại của Anh
Bài viết Wikipedia hiện đang nêu (một số định dạng hơi đơn giản):
AI-1111
: Anguila
ASCN 1ZZ
: Đảo Ascension
STHL 1ZZ
: Thánh Helena
TDCU 1ZZ
: Tristan da Cunha
BBND 1ZZ
: Lãnh thổ Ấn Độ Dương thuộc Anh
BIQQ 1ZZ
: Lãnh thổ Nam Cực thuộc Anh
FIQQ 1ZZ
: Quần đảo Falkland
GX11 1ZZ
: Gibraltar
PCRN 1ZZ
: Quần đảo Pitcairn
SIQQ 1ZZ
: Nam Georgia và Quần đảo Nam Sandwich
TKCA 1ZZ
: Quần đảo Turks và Caicos
BFPO 11
: Akrotiri và Dhekelia
ZZ 11
& GE CX
: Bermuda (theo tài liệu này )
KY1-1111
: Quần đảo Cayman (theo tài liệu này )
VG1111
: Quần đảo Virgin thuộc Anh (theo tài liệu này )
MSR 1111
: Montserrat (theo tài liệu này )
Một regex bao gồm tất cả để chỉ phù hợp với Lãnh thổ hải ngoại của Anh có thể trông như thế này:
Xem regex đang sử dụng ở đây .
^((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 Bưu điện lực lượng Anh
Mặc dù gần đây họ đã thay đổi nó để phù hợp hơn với hệ thống mã bưu chính của Anh thành BF#
(nơi #
đại diện cho một số), chúng được coi là mã bưu điện thay thế tùy chọn . Các mã bưu điện này tuân theo (ed) định dạng củaBFPO
, theo sau là 1-4 chữ số:
Xem regex đang sử dụng tại đây
^BFPO ?\d{1,4}$
3,3 ông già Noel?
Có một trường hợp đặc biệt khác với ông già Noel (như đã đề cập trong các câu trả lời khác): SAN TA1
là một mã bưu điện hợp lệ. Một regex cho điều này rất đơn giản:
^SAN ?TA1$