ZIP、Postcode、PLZ:写给开发者的全球邮编体系详解
如果你的地址表单只有一个五位数字的邮编输入框,外加一条从美国教程里抄来的正则,那么它对世界上大多数地区来说已经是坏的了。英国邮编字母数字混排,日本要七位数字加连字符,荷兰邮编以两个字母结尾,而香港根本没有邮编。这里的每一处差异,都是一个等着被提交的 bug。
本文梳理主要邮编体系的结构、朴素校验最容易翻车的地方,以及如何设计经得起国际化输入考验的表单和数据库。它与多国生成测试数据是天然搭档:想知道你的结账页能不能接受 EC1A 1BB,最快的办法就是粘一个进去看看。
邮编到底编码了什么
邮编本质上是一条路由指令,而不是一个位置。邮政机构发明这套体系是为了机械化分拣:编码告诉分拣机一封信应该送到哪个区域枢纽、交给哪个投递局。邮编与地理位置的相关性只是副作用,不是这套系统做出的承诺。
这一点很重要,因为开发者经常把邮编当成它不是的东西:精确坐标、稳定的行政区划边界,或者放之四海皆准的必填项。投递路线调整时编码会被重新分配,一个编码可能横跨几个城镇,而一栋大型写字楼也可能独占一个编码。
纯数字体系:ZIP、PLZ 及同类
美国 ZIP 是五位数字:首位标识大区(0 在东北部、9 在西海岸),后续数字逐级缩小到处理中心和投递区。可选的 ZIP+4 扩展在后面追加连字符和四位数字,精确到街区或大型建筑,所以校验必须同时接受 62704 和 62704-1234。德国的 Postleitzahl(PLZ)同样是五位数字,两德统一后于 1993 年全国重新划分,首位对应十个邮政大区之一。法国也用五位数字,但有个好记的细节:前两位是省(département)编号,75001 在巴黎、33000 在波尔多。
其他国家的位数则各不相同。中国使用六位数字,从省级逐层细分到投递邮局。日本使用七位数字,写作 NNN-NNNN,例如 100-0001——书面上习惯带连字符,尽管编码本身只是七个数字。澳大利亚只用四位数字,会让默认"至少五位"的校验器措手不及。这些体系虽然都不含字母,照样能击穿简陋的代码:限长五个字符的输入框会截断日本邮编,而"仅限数字"的过滤器会吞掉用户自然输入的连字符。
字母数字混合体系:英国、加拿大与荷兰
英国邮编是经典的压力测试。它分为外码(区域加行政区,如 EC1A)和内码(分区加单元,如 1BB),中间用一个空格分隔:EC1A 1BB。外码长度在两到四个字符之间浮动,因此完整邮编连空格在内为六到八个字符。A9 9AA、A99 9AA、AA9 9AA、AA9A 9AA 等模式都真实存在,这就是为什么手写英国邮编正则是一门盛产隐蔽错误的手艺。
加拿大采用 A9A 9A9 格式,字母与数字交替出现:前三位是转发分拣区(FSA),后三位是本地投递单元(LDU)。字母 D、F、I、O、Q、U 在加拿大邮编中永远不会出现——因为它们在手写和 OCR 识别中容易混淆。这个细节是严格校验器必须编码进去的,生成的测试数据也应当遵守。荷兰使用 NNNN AA 格式,如 1012 AB;邮编加门牌号几乎能唯一确定一个地址,所以荷兰的结账流程往往先要这两个字段,再自动填出街道。
这三套体系都会招来同样的规范化 bug:用户会输入小写、漏掉空格或多打空格。稳健的做法是把输入统一转大写、折叠空白、补回规范空格,而不是直接拒绝 ec1a1bb。
完全没有邮编的国家和地区
有些地区根本不使用邮编。香港没有邮编,邮件按地区和街道路由。阿联酋历史上依赖邮政信箱,其 Makani 系统是建筑物标识符,并非传统意义上的邮编。爱尔兰在 2015 年 Eircode 上线之前一直没有邮编,至今仍有不少爱尔兰用户背不出自己的 Eircode。
对表单设计而言,结论很直接:一个必填、又无法跳过的邮编输入框,会把这些用户挡在你的产品外面。解决办法是让必填规则随所选国家变化,并让数据结构允许存空值或 null 而不把整条记录判为无效。如果你的测试夹具只有美国地址,这条路径就永远不会被执行——"邮编必填"的 bug 正是这样溜进生产环境的。
校验与存储建议
第一,邮编永远存字符串,绝不要存整数。新泽西的 07030 一旦流经整数列就变成 7030,而且这种损坏是无声的:数值看起来仍然合理,直到承运商 API 拒收才暴露。会自作聪明把"看起来像数字"的列转成数值的电子表格和 CSV 管道,同样是重灾区。
第二,不要把某一个国家的正则用到全世界。匹配五位美国数字的模式会拒绝所有英国、加拿大、荷兰和日本邮编。务实的做法是按国家校验:维护一张以国家代码为键的模式查找表,在用户选定国家之后再应用,对其余国家使用"字母、数字、空格、连字符"的宽松兜底模式。先规范化再校验——去首尾空白、转大写、折叠内部空格——并存储规范化后的形式,让比较和去重行为可预测。最后,克制住比邮政机构更严格的冲动:拒掉一个合法邮编会丢掉一位客户,放进一个略有瑕疵的邮编不过是日后改一下而已。
邮编定位的局限与生成数据测试
邮编是一个区域,不是一个点。地理编码服务对某个邮编返回的是该投递区域的质心——它可能落在湖里,甚至完全落在另一个街区。美国 ZIP 尤其危险:它本质是投递路线的集合而非闭合多边形,人口普查局提供的 ZCTA 形状也只是近似。用邮编质心做距离计算、税务查询或"附近门店"推荐,得到的答案通常不离谱、偶尔错得离谱——与其在客服工单里发现这个误差,不如提前把容差写进文档。
这正是多国生成测试数据的价值所在。一批来自美、英、德、日、加、荷兰和香港的地址,一次就能覆盖上文的所有路径:前导零能否在存储层幸存、字母数字混合编码能否通过校验、七位带连字符的编码是否装得进字段宽度、空邮编能否顺利流过一个不许强制必填的表单。由于数据是合成的,你可以批量生成上百条记录接入自动化测试,也可以放心分享失败用例——你的缺陷跟踪器里永远不会出现任何真实住址。
常见问题
能用一条通用正则校验所有邮编吗? +
不能。不存在既接受所有合法邮编又不放进垃圾数据的单一模式。应在用户选定国家后按国家校验,对没有具体规则的国家使用宽松的兜底模式。拒绝合法输入比偶尔放进一个格式欠佳的邮编更糟糕。
为什么邮编必须用字符串存储? +
因为很多邮编带前导零(如 02134、07030),整数类型会无声地丢掉它们;还有大量体系包含字母、空格或连字符,根本无法用数值表示。写入时做规范化的字符串列,是唯一对所有国家都成立的表示方式。
可以把邮编转换成精确坐标吗? +
只能得到近似值。邮编标识的是投递区域或路线,地理编码返回的是区域质心,可能离任何具体地址都很远。用于粗略的距离排序或区域统计没问题,但不适合导航、精确的税务边界判定或任何有法律意义的场景。
表单里的邮编必填项,遇到无邮编国家怎么办? +
让必填规则随所选国家变化。对香港、阿联酋等地区,隐藏该字段或接受空值,并让数据结构允许存 null。发布前应专门用无邮编地区的生成地址测试这条路径。