ผมเพิ่งโพสต์คำตอบให้กับคำถามนี้ในสหราชอาณาจักรรหัสไปรษณีย์สำหรับภาษาอาร์ ฉันค้นพบว่ารูปแบบ regex ของรัฐบาลอังกฤษไม่ถูกต้องและไม่สามารถตรวจสอบรหัสไปรษณีย์ได้อย่างถูกต้อง น่าเสียดายที่คำตอบมากมายที่นี่ขึ้นอยู่กับรูปแบบที่ไม่ถูกต้องนี้
ฉันจะสรุปปัญหาเหล่านี้ด้านล่างและให้นิพจน์ทั่วไปที่แก้ไขซึ่งใช้งานได้
บันทึก
คำตอบของฉัน (และนิพจน์ทั่วไปโดยทั่วไป):
- ตรวจสอบรูปแบบรหัสไปรษณีย์เท่านั้น
- ไม่แน่ใจว่ามีรหัสไปรษณีย์ถูกต้องตามกฎหมายหรือไม่
- สำหรับสิ่งนี้ใช้ API ที่เหมาะสม! ดูคำตอบของ Benสำหรับข้อมูลเพิ่มเติม
หากคุณไม่สนใจเกี่ยวกับregex ที่ไม่ดีและเพียงต้องการข้ามไปยังคำตอบให้เลื่อนลงไปที่ส่วนคำตอบ
Regex ไม่ดี
ไม่ควรใช้นิพจน์ทั่วไปในส่วนนี้
นี่คือ regex ที่ล้มเหลวที่รัฐบาลสหราชอาณาจักรให้นักพัฒนา (ไม่แน่ใจว่าลิงก์นี้จะใช้งานได้นานเท่าใด แต่คุณสามารถดูได้ในเอกสารการโอนข้อมูลจำนวนมาก ):
^([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})$
ปัญหาที่เกิดขึ้น
ปัญหาที่ 1 - คัดลอก / วาง
ดู regex ในการใช้งานที่นี่
ตามที่นักพัฒนาหลายคนน่าจะทำพวกเขาก็อปปี้ / วางรหัส (โดยเฉพาะอย่างยิ่งการแสดงออกปกติ) และวางพวกเขาคาดหวังให้พวกเขาทำงาน แม้ว่าจะดีในทางทฤษฎี แต่ก็ล้มเหลวในกรณีนี้เนื่องจากการคัดลอก / วางจากเอกสารนี้เปลี่ยนอักขระหนึ่งตัว (ช่องว่าง) เป็นอักขระขึ้นบรรทัดใหม่ตามที่แสดงด้านล่าง:
^([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 จะไม่จับคู่รหัสไปรษณีย์กับช่องว่างในนั้น (นอกเหนือจากGIR 0AA
รหัสไปรษณีย์)
ในการแก้ไขปัญหานี้อักขระขึ้นบรรทัดใหม่ควรถูกแทนที่ด้วยอักขระเว้นวรรค:
^([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})$
^
ปัญหาที่ 2 - ขอบเขต
ดู regex ในการใช้งานที่นี่
^([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 เชื่อมโยง regex ไม่ถูกต้อง ทุกคนที่ใช้ regex นี้เพื่อตรวจสอบความถูกต้องของรหัสไปรษณีย์อาจแปลกใจหากค่าที่fooA11 1AA
ได้รับผ่าน นั่นเป็นเพราะพวกเขาได้ยึดจุดเริ่มต้นของตัวเลือกแรกและจุดสิ้นสุดของตัวเลือกที่สอง (เป็นอิสระจากกัน) ตามที่ชี้ใน regex ข้างต้น
สิ่งนี้หมายความว่า^
(ยืนยันตำแหน่งที่จุดเริ่มต้นของบรรทัด) ใช้งานได้กับตัวเลือกแรก([Gg][Ii][Rr] 0[Aa]{2})
เท่านั้นดังนั้นตัวเลือกที่สองจะตรวจสอบความถูกต้องของสตริงใด ๆ ที่ลงท้ายด้วยรหัสไปรษณีย์ (ไม่ว่าจะเกิดอะไรขึ้นมาก่อน)
ในทำนองเดียวกันตัวเลือกแรกไม่ได้ยึดกับท้ายบรรทัด$
ดังนั้นจึงGIR 0AAfoo
เป็นที่ยอมรับเช่นกัน
^([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})$
ในการแก้ไขปัญหานี้ควรห่อทั้งสองตัวเลือกไว้ในกลุ่มอื่น (หรือกลุ่มที่ไม่ได้จับภาพ) และจุดยึดที่อยู่รอบ ๆ :
^(([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}))$
^^ ^^
ปัญหา 3 - ชุดอักขระที่ไม่เหมาะสม
ดู regex ในการใช้งานที่นี่
^([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 หายไป-
ที่นี่เพื่อระบุช่วงของอักขระ ตามที่ปรากฏหากรหัสไปรษณีย์อยู่ในรูปแบบANA NAA
(โดยที่A
หมายถึงตัวอักษรและN
แทนตัวเลข) และเริ่มต้นด้วยสิ่งอื่นที่ไม่ใช่A
หรือZ
มันจะล้มเหลว
หมายความว่ามันจะตรงA1A 1AA
และแต่ไม่Z1A 1AA
B1A 1AA
ในการแก้ไขปัญหานี้-
ควรใส่อักขระระหว่างA
และZ
ในชุดอักขระที่เกี่ยวข้อง:
^([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})$
^
ปัญหา 4 - ชุดอักขระทางเลือกไม่ถูกต้อง
ดู regex ในการใช้งานที่นี่
^([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})$
^
ฉันสาบานว่าพวกเขาไม่ได้ทดสอบสิ่งนี้ก่อนที่จะเผยแพร่บนเว็บ พวกเขาทำให้ชุดอักขระที่ไม่ถูกต้องเป็นตัวเลือก พวกเขาทำตัว[0-9]
เลือกในตัวเลือกย่อยที่สี่ของตัวเลือก 2 (กลุ่ม 9) นี้จะช่วยให้ regex AAA 1AA
ให้ตรงกับรหัสไปรษณีย์ที่จัดรูปแบบไม่ถูกต้องเช่น
ในการแก้ไขปัญหานี้ให้เลือกคลาสอักขระถัดไปแทน (และต่อมาทำให้ชุด[0-9]
ตรงกันทุกครั้ง):
^([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})$
^
ปัญหาที่ 5 - ประสิทธิภาพ
ประสิทธิภาพของ regex นี้แย่มาก ก่อนอื่นพวกเขาวางตัวเลือกรูปแบบที่มีโอกาสน้อยที่สุดให้ตรงGIR 0AA
กับจุดเริ่มต้น มีผู้ใช้กี่คนที่จะมีรหัสไปรษณีย์นี้เทียบกับรหัสไปรษณีย์อื่น ๆ อาจจะไม่เคย? ซึ่งหมายความว่าทุกครั้งที่ใช้งาน regex จะต้องใช้ตัวเลือกนี้เสียก่อนจึงจะดำเนินการตามตัวเลือกถัดไป เพื่อดูว่าประสิทธิภาพได้รับผลกระทบอย่างไรให้ตรวจสอบจำนวนขั้นตอนที่regex ดั้งเดิมใช้ (35) เทียบกับregex เดียวกันหลังจากพลิกตัวเลือก (22)
ปัญหาที่สองที่มีประสิทธิภาพเป็นเพราะโครงสร้างทั้งหมดของ regex ไม่มีการย้อนจุดในแต่ละตัวเลือกหากล้มเหลว วิธีที่โครงสร้าง regex ปัจจุบันสามารถทำให้ง่ายขึ้นอย่างมาก ฉันให้การแก้ไขในส่วนคำตอบ
ปัญหาที่ 6 - ช่องว่าง
ดูการใช้งาน regex ที่นี่
สิ่งนี้อาจไม่ถือว่าเป็นปัญหาแต่ก็สร้างความกังวลให้กับนักพัฒนาส่วนใหญ่ ช่องว่างใน regex ไม่ใช่ตัวเลือกซึ่งหมายความว่าผู้ใช้ที่ป้อนรหัสไปรษณีย์จะต้องวางช่องว่างในรหัสไปรษณีย์ นี่คือการแก้ไขที่ง่ายเพียงเพิ่ม?
หลังช่องว่างเพื่อแสดงเป็นตัวเลือก ดูส่วนคำตอบสำหรับการแก้ไข
ตอบ
1. แก้ไข Regex ของรัฐบาลอังกฤษ
แก้ไขปัญหาทั้งหมดที่สรุปไว้ในส่วนของปัญหาและทำให้รูปแบบง่ายขึ้นให้รูปแบบต่อไปนี้สั้นลงและกระชับยิ่งขึ้น เรายังสามารถลบกลุ่มส่วนใหญ่ได้เนื่องจากเราตรวจสอบรหัสไปรษณีย์โดยรวม (ไม่ใช่แต่ละส่วน):
ดูการใช้งาน regex ที่นี่
^([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})$
สิ่งนี้สามารถย่อให้สั้นลงได้โดยลบช่วงทั้งหมดออกจากหนึ่งในกรณี (ตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็ก) และใช้แฟล็กตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ หมายเหตุ : บางภาษาไม่มีภาษาให้ใช้ภาษาที่มีความยาวมากกว่าด้านบน แต่ละภาษาใช้การตั้งค่าตัวพิมพ์เล็กและตัวพิมพ์ใหญ่แตกต่างกัน
ดู regex ในการใช้งานที่นี่
^([A-Z][A-HJ-Y]?[0-9][A-Z0-9]? ?[0-9][A-Z]{2}|GIR ?0A{2})$
เปลี่ยนให้สั้นลง[0-9]
ด้วย\d
(ถ้าเครื่องยนต์ regex ของคุณรองรับ):
ดู regex ในการใช้งานที่นี่
^([A-Z][A-HJ-Y]?\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
2. รูปแบบที่เรียบง่าย
โดยไม่ทำให้อักขระตัวอักษรเฉพาะเจาะจงสามารถใช้สิ่งต่อไปนี้ได้ (โปรดจำไว้ว่า simplifications จาก1 การแก้ไข Regex ของรัฐบาลสหราชอาณาจักรได้ถูกนำมาใช้ที่นี่ด้วย):
ดู regex ในการใช้งานที่นี่
^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
และยิ่งไปกว่านั้นถ้าคุณไม่สนใจกรณีพิเศษGIR 0AA
:
^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$
3. รูปแบบที่ซับซ้อน
ฉันจะไม่แนะนำการตรวจสอบรหัสไปรษณีย์มากเกินไปเนื่องจากอาจมีพื้นที่ใหม่เขตและตำบลใหม่ปรากฏขึ้น ณ เวลาใด ๆ สิ่งที่ฉันจะแนะนำให้ทำอาจเพิ่มการรองรับขอบเคส บางกรณีพิเศษที่มีอยู่และมีการระบุไว้ในบทความวิกิพีเดียนี้
นี่คือ regexes ที่ซับซ้อนซึ่งรวมถึงส่วนย่อยของ3 (3.1, 3.2, 3.3)
เกี่ยวกับรูปแบบใน1. การแก้ไข Regex ของรัฐบาลสหราชอาณาจักร :
ดูการใช้งาน regex ที่นี่
^(([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)$
และเกี่ยวข้องกับ2. รูปแบบประยุกต์ :
ดูการใช้งาน regex ที่นี่
^(([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 ดินแดนโพ้นทะเลของอังกฤษ
บทความ Wikipedia ปัจจุบันระบุว่า (บางรูปแบบง่ายขึ้นเล็กน้อย):
AI-1111
: Anguila
ASCN 1ZZ
: เกาะสวรรค์
STHL 1ZZ
: เซนต์เฮเลน่า
TDCU 1ZZ
: Tristan da Cunha
BBND 1ZZ
: มณฑลบริติชอินเดียนโอเชียน
BIQQ 1ZZ
: มณฑลบริติชแอนตาร์กติก
FIQQ 1ZZ
: หมู่เกาะฟอล์คแลนด์
GX11 1ZZ
ยิบรอลตาร์
PCRN 1ZZ
: หมู่เกาะพิตแคร์น
SIQQ 1ZZ
: เกาะเซาท์จอร์เจียและหมู่เกาะเซาท์แซนด์วิช
TKCA 1ZZ
: หมู่เกาะเติกส์และหมู่เกาะเคคอส
BFPO 11
: Akrotiri และ Dhekelia
ZZ 11
& GE CX
: เบอร์มิวดา (ตามเอกสารนี้ )
KY1-1111
: หมู่เกาะเคย์แมน (ตามเอกสารนี้ )
VG1111
: หมู่เกาะบริติชเวอร์จิน (อ้างอิงจากเอกสารนี้ )
MSR 1111
: มอนต์เซอร์รัต (ตามเอกสารนี้ )
regex ที่ครอบคลุมเพื่อให้ตรงกับดินแดนโพ้นทะเลของอังกฤษอาจมีลักษณะเช่นนี้:
ดู regex ในการใช้งานที่นี่
^((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 ที่ทำการไปรษณีย์อังกฤษ
แม้ว่าพวกเขาจะได้รับการเปลี่ยนแปลงเมื่อเร็ว ๆ นี้เพื่อให้สอดคล้องกับระบบรหัสไปรษณีย์ของอังกฤษเป็นBF#
(ซึ่ง#
หมายถึงตัวเลข) ได้ดีขึ้น แต่พวกเขาก็ถือว่าเป็นรหัสทางเลือกเพิ่มเติม รหัสไปรษณีย์เหล่านี้ติดตาม (ed) รูปแบบของBFPO
ตามด้วยตัวเลข 1-4 หลัก:
ดูการใช้งาน regex ที่นี่
^BFPO ?\d{1,4}$
3.3 ซานต้า?
มีอีกกรณีพิเศษกับซานต้า (ดังที่ได้กล่าวไว้ในคำตอบอื่น ๆ ): SAN TA1
เป็นรหัสไปรษณีย์ที่ถูกต้อง regex สำหรับเรื่องนี้ง่ายมาก:
^SAN ?TA1$