把 GB/T 17710 写成代码:8 个算法 + 选型指南(GB/T 17710 续三)
前几篇把 GB/T 17710 的公式和人工手算讲透了。这一篇把它落成可运行的代码——一套覆盖标准全部 8 个算法的 Python / JavaScript 库,并附上选型指南:你的场景到底该用哪个算法。所有算法都用公开测试向量验证过(Eurocode 血袋号、gb-util 库、科普中国例题),两个语言版本输出完全一致。
一、代码长什么样
标准只有两条主干逻辑:纯系统用"加权求和取模"(多项式法),混合系统用"乘积折叠"(递归法)。库里把它们各自封装成一个类,再用参数实例化出 8 个算法。
纯系统 MOD 11-2 的核心(Python):
def calc_mod112(data: str) -> str:
n = 0
for i, ch in enumerate(reversed(data)):
n += int(ch) * (2 ** i) # 最右位权重 2^0
v = (1 - 2 * n) % 11 # 校验值
check = "X" if v == 10 else str(v) # 10 用 X 表示
return data + check
混合系统 MOD 11,10 的核心(乘积折叠):
def calc_mod1110(data: str) -> str:
p = 10
for ch in data:
d = int(ch)
s = (d + p) % 10
if s == 0:
s = 10
p = (s * 2) % 11
return data + str((1 - p) % 10)
完整的库把 8 个算法都接好了 calc / is_valid / validate 接口,并带单元测试。上面的片段只是"最小可读版",真实库还处理了字母字母数字映射、2 位校验拆分、负数取模等细节。
二、8 个算法的"身份证"
按"字符集 + 校验位数"把 8 个算法摊开(博客渲染不支持表格,用分组清单):
纯系统(单模数,加权求和)
- MOD 11-2:数字 0-9(+X),1 位,模 11、基 2
- MOD 37-2:0-9、A-Z(+*),1 位,模 37、基 2
- MOD 97-10:数字,2 位,模 97、基 10
- MOD 661-26:纯字母 A-Z,2 位,模 661、基 26
- MOD 1271-36:0-9、A-Z,2 位,模 1271、基 36
混合系统(双模数,乘积折叠,乘数统一为 2)
- MOD 11,10:数字,1 位(M=10)
- MOD 27,26:纯字母 A-Z,1 位(M=26)
- MOD 37,36:0-9、A-Z,1 位(M=36)
关键点:混合系统在标准里只有这三种,且都只出 1 位。需要 2 位校验时,标准走的是纯系统(MOD 97-10 等),不是混合。
三、选型指南:到底用哪个
一句口诀:先看字符集,再看要几位,最后看能不能接受补字符。
- 只用数字、1 位、能接受 X 补字符 → MOD 11-2(最经典,与身份证同族)
- 只用数字、1 位、校验位必须还是数字(不能出现 X)→ MOD 11,10(税号、OIB、血袋号都用它)
- 只用数字、要 2 位 → MOD 97-10(IBAN 同思路、强度更高)
- 字母数字、1 位、**能接受 * 补字符 → MOD 37-2**
- 字母数字、1 位、校验位必须还是字母数字 → MOD 37,36
- 纯字母、1 位 → MOD 27,26
- 字母数字、要 2 位 → MOD 1271-36
- 纯字母、要 2 位 → MOD 661-26
混合系统的价值,正是"校验字符永远留在原生字符集"——当你规定标识符里不能出现 X、* 这类补字符时(税号、血袋号、ISBT 都是如此),它就是唯一选择。
四、验证:拿公开向量过一遍
库里的测试不是自己编的,而是对照公开资料:
- MOD 11,10 血袋号
"0823"→"08235"(Eurocode 例题) - MOD 11-2
"0794"→"07940"(科普中国例题) - MOD 37-2
"1234ABCD"→"V"、MOD 97-10"794"→"44"、MOD 661-26"ABCD"→"KN"、MOD 1271-36"ISO79"→"3W"(gb-util 库向量) - MOD 27,26
"JEJLMGJ"→"S"、MOD 37,36 长串 →"I"
两个语言版本都跑通了全部用例,并且能正确拦截"把某一位写错"的脏数据(篡改后 is_valid 返回 False)。
五、怎么用
Python:
from iso7064 import mod11_2, mod37_36
mod11_2.calc("0794") # "07940"
mod11_2.is_valid("07940") # True
mod37_36.calc("B7Q3SFTUSH2QN7BIXBPMNZAM") # 末尾补 "I"
JavaScript(ES Module):
import { mod11_10, mod1271_36 } from "./iso7064.js";
mod11_10.calc("0794"); // "07945"
mod1271_36.is_valid("ISO793W"); // true
完整代码与测试随本文提供(Python + JS 双版本),直接拿去用即可。