各国身份证号码长度是设计表单时最容易被低估的一件事:有人按自己熟悉的那一种把字段长度固定下来,上线之后才发现换一个国家就整批数据进不来。号码的长短、允许出现的字符、以及里面有没有校验位,每个发行国都有自己的习惯,而且同一个国家还可能换过格式。读完这篇,你知道该把长度当成长度还是当成规则,也能给自己的校验逻辑留出兜底的空间。
长度可以从个位数一直到十几位
有的国家发的是很短的编号,个位数就能唯一标识一个人;有的国家则用十几位的长串,把地区、出生日期与序列号全部编进去。只按最常见的那一种去写校验,等于默认全世界都用同一套规则。
差别不只体现在位数上,还体现在「位数是否固定」这一点上。有的体系给每个人发同样长度的号码,有的则允许在位数上浮动,比如短号用完之后自然延长一位。把长度上限设成一个偏小的值,最先被挡住的往往是真实的合法输入。
对表单来说,这个字段更适合按字符串处理:不做数值运算,不关心前导零,也不假设它总能用整数表示。只要其中一位落在零上就被截掉,这类错误在导入环节很难被发现。
字符集不止纯数字这一种
纯数字是最常见的一类,但绝不是唯一的一类。有的允许字母与数字混排,有的只在特定位置允许字母,有的把字母当作校验字符使用。同一个字符在不同体系里含义不同,因此不能简单地写一条「只允许数字」的规则套到所有国家。
大小写也是常被忽略的细节。有的体系要求统一大写,有的对大小写不敏感,有的则把这两种写法视为不同的号码。稳妥的处理方式是在保存前统一规范成一种形态,同时接受用户以另一种形态输入的原始值。
| 类型 | 常见特征 | 校验时要留意 |
|---|---|---|
| 纯数字 | 位数固定,便于分段书写 | 前导零不能丢 |
| 字母数字混合 | 字母常出现在固定位置 | 统一大小写与分隔符 |
| 带校验位 | 末一两位由其余位算出 | 校验位不代表身份真实 |
| 编入出生日期 | 号码中含日期片段 | 日期片段未必是本人出生日 |
校验位说明什么,不说明什么?
很多编号体系会在末尾留一两位作为校验位,用加权求和取模一类的算术从其余数字算出来,作用是发现手误与相邻数字写反。巴西的个人税号就是这种结构,末两位由前面的位算出;加拿大社会保险号也属于位数固定、规则明确的一类。
但校验位只能证明「这串数字内部自洽」,不能证明它属于某个人。一串完全凭空构造的号码照样可以通过校验位的检查,因为它本来就是按同一套算术造出来的。所以把通过校验位当成身份核实的替代品,是一个方向性错误:真正需要确认身份的场合依赖的是签发与核验流程,而不是一个算术结果。
也正因为如此,测试数据里让号码通过校验位是有意义的——它能让你的流程走得更远,暴露下游的字段处理问题——但这与号码是否指向真人毫无关系。
同一个国家也会换格式吗?
会。编号体系会随着行政改革、系统升级或者覆盖范围扩大而调整,有的国家在旧号用尽后追加位数,有的把原本分散的编号统一成一套新格式,也有的让新旧格式在若干年里并行存在。
这意味着你的数据里可能同时存在同一个国家的两种合法形态。校验规则如果只认其中一种,另一部分用户就会被误判。常见的应对办法是让校验规则带上生效时间的维度,或者干脆放宽为「符合其中任一已知形态即通过」。这一点在测试数据里也要体现:同一个国家的样本最好同时覆盖旧格式与新格式,而不是只留一种。想对照具体国家的地址与证件字段差异,日本与德国的国家页里能看到字段层级的不同组织方式。
一个国家的规则要单独写吗?
在规则表里新增一个国家时,值得先问四个问题,而不是直接复制上一个国家的配置。第一,位数是否固定,还是允许在某个区间内浮动;第二,允许出现的字符有哪些,字母是否只在特定位置出现;第三,末尾有没有校验位,如果有,算法是不是公开可查的;第四,号码里是否编入了出生日期,如果编入了,那它是不是必然等于持有人的生日。
最后一条尤其容易被误解。有些体系会把出生日期直接编进号码,但号码里的那一段未必就是持有人本人的出生日期,也可能只是发行时的某种批次标记。因此不能反过来用号码里的日期去覆盖记录里已经填好的生日字段:一旦两者不一致,你无法判断是哪一个错了,而强行同步会让数据变得更不可信。
把这四条写成一行配置而不是一段代码,好处是新增国家时改动可控,也便于让别人复核你的理解是否正确。规则的来源应当是可引用的公开说明,而不是凭印象补出来的——写错一条保留规则,可能会把整批合法用户挡在门外。
给开发者:把长度当规则,而不是当常量
第一,不要在数据库层把长度写成固定值。给这个字段留出足够宽的空间,长度约束放在应用层按国家判断,这样换国家只需要换规则,不需要改表结构。
第二,校验逻辑按发行国分派。把「某国怎么校验」集中到一处配置里,而不是散落在各个表单组件中。这样新增一个国家只改一个地方,也方便为每种形态写固定的回归用例。同一批数据里两条记录用同一种编号形态,往往是因为生成时没有按国家分派,字段一致性那篇身份数据一致性讲了怎么把这类问题挡在测试之前。
第三,永远留一条「未知格式」的兜底分支。新国家、旧格式、临时签发的编号都可能不在你的规则表里,这时正确的行为是接受并标记为待确认,而不是直接拒绝。一条把合法数据挡在门外的校验规则,比不校验更糟。
第四,准备边界样本的时候,把注意力放在形态而不是具体数值上:最短的可能长度、最长的可能长度、带字母的、带前导零的、带分隔符的、以及大小写不一致的。合成出来的号码只能用于测试与演示,它不能让人冒充真实公民,也不能用来通过实名核验、开户或者签署任何有法律效力的文件,这一点和使用真实号码一样必须守住。可复现的样本怎么组织,见测试固定装置里的身份数据。
下一步
要一批形态正确、字段之间不矛盾的号码,直接在身份信息生成器里按国家批量生成,导出后放进你的测试库;生成与校验的取舍另有一篇美国社会安全号码格式讲了保留段该怎么处理。