Saya baru-baru diposting jawaban untuk pertanyaan ini pada kode pos Inggris untuk bahasa R . Saya menemukan bahwa pola regex Pemerintah Inggris salah dan gagal dengan benar memvalidasi beberapa kode pos. Sayangnya, banyak jawaban di sini didasarkan pada pola yang salah ini.
Saya akan menguraikan beberapa masalah di bawah ini dan memberikan ekspresi reguler yang direvisi yang benar - benar berfungsi.
Catatan
Jawaban saya (dan ekspresi reguler secara umum):
- Hanya memvalidasi format kode pos .
- Tidak memastikan bahwa kode pos ada secara sah .
- Untuk ini, gunakan API yang sesuai! Lihat jawaban Ben untuk info lebih lanjut.
Jika Anda tidak peduli dengan regex yang buruk dan hanya ingin melewatkan jawaban, gulir ke bawah ke bagian Jawab .
Regex Buruk
Ekspresi reguler di bagian ini tidak boleh digunakan.
Ini adalah regex yang gagal yang disediakan oleh pemerintah Inggris untuk pengembang (tidak yakin berapa lama tautan ini akan naik, tetapi Anda dapat melihatnya di dokumentasi Transfer Data Massal mereka ):
^([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})$
Masalah
Masalah 1 - Salin / Tempel
Lihat regex yang digunakan di sini .
Seperti yang mungkin dilakukan banyak pengembang, mereka menyalin / menempelkan kode (terutama ekspresi reguler) dan menempelnya agar mereka berfungsi. Walaupun ini bagus secara teori, ia gagal dalam kasus khusus ini karena menyalin / menempel dari dokumen ini sebenarnya mengubah salah satu karakter (spasi) menjadi karakter baris baru seperti yang ditunjukkan di bawah ini:
^([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})$
Hal pertama yang akan dilakukan kebanyakan pengembang adalah menghapus baris baru tanpa berpikir dua kali. Sekarang regex tidak akan cocok dengan kode pos dengan spasi di dalamnya (selainGIR 0AA
kode pos).
Untuk memperbaiki masalah ini, karakter baris baru harus diganti dengan karakter spasi:
^([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})$
^
Masalah 2 - Batas
Lihat regex yang digunakan di sini .
^([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})$
^^ ^ ^ ^^
Kode pos regex secara tidak patut jangkar regex. Siapa pun yang menggunakan regex ini untuk memvalidasi kode pos mungkin akan terkejut jika nilainya sepertifooA11 1AA
melewati. Itu karena mereka telah meletakkan awal opsi pertama dan akhir opsi kedua (terlepas dari satu sama lain), sebagaimana ditunjukkan dalam regex di atas.
Ini artinya ^
(menegaskan posisi di awal baris) hanya berfungsi pada opsi pertama ([Gg][Ii][Rr] 0[Aa]{2})
, jadi opsi kedua akan memvalidasi string apa pun yang berakhir dengan kode pos (terlepas dari apa yang datang sebelumnya).
Demikian pula, opsi pertama tidak berlabuh ke akhir baris $
, jadi GIR 0AAfoo
juga diterima.
^([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})$
Untuk memperbaiki masalah ini, kedua opsi harus dibungkus dalam kelompok lain (atau kelompok yang tidak menangkap) dan jangkar ditempatkan di sekitarnya:
^(([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}))$
^^ ^^
Masalah 3 - Set Karakter Tidak Benar
Lihat regex yang digunakan di sini .
^([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 tidak ada di -
sini untuk menunjukkan serangkaian karakter. Seperti berdiri, jika kode pos dalam format ANA NAA
(di mana A
mewakili huruf dan N
mewakili angka), dan itu dimulai dengan apa pun selain A
atauZ
, itu akan gagal.
Itu berarti akan cocok A1A 1AA
dan Z1A 1AA
, tetapi tidak B1A 1AA
.
Untuk memperbaiki masalah ini, karakter -
harus ditempatkan di antara A
dan Z
di set karakter masing-masing:
^([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})$
^
Masalah 4 - Set Karakter Opsional Salah
Lihat regex yang digunakan di sini .
^([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})$
^
Saya bersumpah mereka bahkan tidak menguji hal ini sebelum mempublikasikannya di web. Mereka membuat set karakter yang salah menjadi opsional. Mereka membuat [0-9]
opsi di sub-opsi keempat opsi 2 (grup 9). Ini memungkinkan regex untuk mencocokkan kode pos yang salah diformat seperti AAA 1AA
.
Untuk memperbaiki masalah ini, buat kelas karakter selanjutnya sebagai opsional (dan kemudian buat pasangan yang [0-9]
cocok persis sekali):
^([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})$
^
Masalah 5 - Kinerja
Kinerja pada regex ini sangat buruk. Pertama, mereka menempatkan opsi pola yang paling tidak cocok untuk dicocokkan GIR 0AA
di awal. Berapa banyak pengguna yang mungkin memiliki kode pos ini dibandingkan dengan kode pos lainnya; mungkin tidak pernah? Ini berarti setiap kali regex digunakan, ia harus menghabiskan opsi ini terlebih dahulu sebelum melanjutkan ke opsi berikutnya. Untuk melihat bagaimana kinerja terpengaruh, periksa jumlah langkah yang diambil regex asli (35) terhadap regex yang sama setelah membalik opsi (22).
Masalah kedua dengan kinerja adalah karena cara seluruh regex terstruktur. Tidak ada gunanya menelusuri kembali setiap opsi jika ada yang gagal. Cara regex saat ini disusun dapat sangat disederhanakan. Saya memberikan perbaikan untuk ini di bagian Jawaban .
Masalah 6 - Spasi
Lihat regex yang digunakan di sini
Ini mungkin tidak dianggap sebagai masalah , tetapi itu menimbulkan kekhawatiran bagi sebagian besar pengembang. Spasi di regex tidak opsional, yang berarti pengguna memasukkan kode pos mereka harus menempatkan spasi di kode pos. Ini adalah perbaikan yang mudah dengan hanya menambahkan ?
setelah spasi untuk menjadikannya opsional. Lihat bagian Jawaban untuk perbaikan.
Menjawab
1. Memperbaiki Regex Pemerintah Inggris
Memperbaiki semua masalah yang diuraikan di bagian Masalah dan menyederhanakan pola menghasilkan pola berikut, lebih pendek, lebih ringkas. Kami juga dapat menghapus sebagian besar grup karena kami memvalidasi kode pos secara keseluruhan (bukan bagian individual):
Lihat regex yang digunakan di sini
^([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})$
Ini lebih lanjut dapat dipersingkat dengan menghapus semua rentang dari salah satu kasus (huruf besar atau kecil) dan menggunakan bendera case-insensitive. Catatan : Beberapa bahasa tidak memiliki satu, jadi gunakan yang lebih panjang di atas. Setiap bahasa mengimplementasikan flag ketidaksensitifan kasus secara berbeda.
Lihat regex yang digunakan di sini .
^([A-Z][A-HJ-Y]?[0-9][A-Z0-9]? ?[0-9][A-Z]{2}|GIR ?0A{2})$
Lebih pendek lagi menggantikan [0-9]
dengan \d
(jika mesin regex Anda mendukungnya):
Lihat regex yang digunakan di sini .
^([A-Z][A-HJ-Y]?\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
2. Pola Sederhana
Tanpa memastikan karakter alfabet tertentu, berikut ini dapat digunakan (perlu diingat penyederhanaan dari 1. Memperbaiki Regex Pemerintah Inggris juga telah diterapkan di sini):
Lihat regex yang digunakan di sini .
^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
Dan lebih jauh lagi jika Anda tidak peduli dengan kasus khusus GIR 0AA
:
^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$
3. Pola rumit
Saya tidak akan menyarankan verifikasi kode pos yang berlebih karena Area, Distrik, dan Sub-distrik baru dapat muncul kapan saja. Apa yang akan saya sarankan berpotensi melakukan, ditambahkan dukungan untuk tepi-kasus. Beberapa kasus khusus ada dan diuraikan dalam artikel Wikipedia ini .
Berikut adalah regex kompleks yang mencakup sub-bagian 3. (3.1, 3.2, 3.3).
Sehubungan dengan pola dalam 1. Memperbaiki Regex Pemerintah Inggris :
Lihat regex yang digunakan di sini
^(([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)$
Dan terkait dengan 2. Pola Sederhana :
Lihat regex yang digunakan di sini
^(([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 Wilayah Luar Negeri Britania
Artikel Wikipedia saat ini menyatakan (beberapa format sedikit disederhanakan):
AI-1111
: Anguila
ASCN 1ZZ
: Pulau Ascension
STHL 1ZZ
: Saint Helena
TDCU 1ZZ
: Tristan da Cunha
BBND 1ZZ
: Wilayah Samudra Hindia Britania
BIQQ 1ZZ
: Wilayah Antartika Britania
FIQQ 1ZZ
: Kepulauan Falkland
GX11 1ZZ
: Gibraltar
PCRN 1ZZ
: Kepulauan Pitcairn
SIQQ 1ZZ
: Georgia Selatan dan Kepulauan Sandwich Selatan
TKCA 1ZZ
: Kepulauan Turks dan Caicos
BFPO 11
: Akrotiri dan Dhekelia
ZZ 11
& GE CX
: Bermuda (sesuai dengan dokumen ini )
KY1-1111
: Kepulauan Cayman (menurut dokumen ini )
VG1111
: Kepulauan Virgin Britania Raya (menurut dokumen ini )
MSR 1111
: Montserrat (menurut dokumen ini )
Regex yang mencakup semua untuk mencocokkan hanya Wilayah Luar Negeri Inggris yang akan terlihat seperti ini:
Lihat regex yang digunakan di sini .
^((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 Kantor Pos Pasukan Inggris
Meskipun mereka baru-baru ini mengubahnya untuk menyelaraskan dengan sistem kode pos Inggris lebih baik BF#
(di mana #
mewakili angka), mereka dianggap sebagai kode pos alternatif opsional . Kode pos ini mengikuti (ed) format BFPO
, diikuti oleh 1-4 digit:
Lihat regex yang digunakan di sini
^BFPO ?\d{1,4}$
3.3 Santa?
Ada kasus khusus lain dengan Santa (seperti yang disebutkan dalam jawaban lain): SAN TA1
adalah kode pos yang valid. Regex untuk ini sangat sederhana:
^SAN ?TA1$