IBAN 校验与解析

在浏览器本地校验国际银行账号:国家代码、注册长度、ISO 7064 MOD 97-10 校验位,并输出结构拆解与打印格式。

这个账号在结构上成立吗? 粘贴 IBAN,带不带空格都可以。校验全程在浏览器本地完成:先比对 SWIFT 注册表中的国家代码,再核对总长度,最后计算 ISO 7064 MOD 97-10 校验位。

国家
校验位
BBAN
长度

IBAN 校验到底在做什么

本国账号外面套了一层壳

IBAN 并不是一种全新的账号,它就是原有的国内账号(称为 BBAN),在前面拼上四个字符:两位 ISO 3166 国家代码和两位校验位。这四个字符之后的内容,完全沿用各国银行体系原本的编号方式——这正是长度差异如此之大的原因。挪威的 IBAN 是 15 位,马耳他的是 31 位,两者都完全正确。

这种长度差异构成了第一道真正的校验,也是最常被跳过的一步。每个国家都在 SWIFT 注册了确切的总长度,这是固定值而非上限。德国 IBAN 永远是 22 位。如果你收到一个 DE 开头的 21 位字符串,那无论校验位算不算得过都是错的——因为让字符串变短的错误,校验位根本"看不到"缺失的那个字符。先查国家长度还有一个好处:给出的错误提示远比一句笼统的"校验失败"有用。

MOD 97-10 校验位

校验算法来自 ISO 7064,思路相当优雅:把前四个字符挪到末尾,把每个字母替换成数字(A 为 10,一直到 Z 为 35),然后把整串当作一个巨大的整数来读。如果这个整数除以 97 的余数恰好是 1,IBAN 就通过。

麻烦在于这个数远超任何语言的原生整数类型,常常有八十位以上。实现上不必引入大数库,而是分段取模:从左到右遍历字符串,每处理一位就只保留对 97 取模后的运行值。由于模运算对逐位构造具有分配性,最终余数与用完整数字计算的结果完全一致,而代价只是一次遍历、零额外内存分配。本工具正是这么做的。

之所以选择 MOD 97-10,是因为对于仅仅两个校验字符来说它异常强健:能检出所有单字符替换、所有相邻两字符换位,以及绝大多数更长的乱序。它检测不到的错误形态非常罕见,剩余风险主要来自"账号本身并不存在"这一层。

校验通过不代表什么

结构有效不等于账户存在。一个校验位正确的 IBAN,可能指向一个已销户的账户、一个从未开立的账户,或一家早已被合并掉的银行。唯一能确认钱会到账的方式是发起转账,或使用银行提供的验证服务。请把校验当作录入环节的错别字拦截,而不是收款方真实性的证明。

格式同样需要注意。注册表定义的电子格式是不含空格的连续字符串,支付文件里传输、数据库里存储都必须用这一种。每四位一组的形式只用于纸面打印,目的是帮人准确抄写长串数字。正确做法是:录入时归一化,存储用无空格形式,仅在展示时再把空格加回去。

开源说明:使用原生 JavaScript 实现,不依赖第三方库。

常见问题

校验位正确是否意味着账户存在?
不是。校验位只能证明字符串内部自洽,没有输错或换位。它背后的账户可能已经销户,也可能从未开立。只有收款银行才能确认一个 IBAN 是否有效在用。
为什么 IBAN 长度各不相同?
因为前四位之后的部分就是各国原有的国内账号,而这些编号在 IBAN 出现之前很久就已在各国内部标准化了。每个国家向 SWIFT 注册了自己的固定长度,从挪威的 15 位到圣卢西亚的 32 位不等。
存储 IBAN 时要不要带空格?
不要。应存储电子格式,即不含空格和标点的连续字符串,支付系统预期的也是这一种。每四位分组只是打印习惯,渲染时再加、录入时去掉即可。
BBAN 部分是什么?
即基本银行账号,指国家代码和校验位之后的全部内容。它的内部结构因国家而异,通常编码了银行标识、分行代码和账号,有时还带有本国自己的校验位。
我的 IBAN 会被发送到服务器吗?
不会。长度查表和 MOD 97-10 计算都在浏览器内完成,页面不会对你的输入发起任何网络请求。断网状态下工具照常工作。
位数明明正确,为什么还是校验失败?
几乎总是有一个字符输错或换位了。MOD 97-10 能检出所有单字符错误和相邻换位,所以在长度正确的前提下失败,说明必有一个字符不对。请重新核对原件,尤其注意容易混淆的 0 与 O、1 与 I。