01 - 身份证验证算法
18位的公民身份号码是特征组合码,前17位是数字本体码,最后1位是校验码。其中,前17位数字本体码从左到右可分为三段:
- 地址码:占6位。地址码表示编码对象常住户口所在地的行政区域划分代码;
- 出生日期码,占8位。出生日期码表示编码对象出生的年月日;
- 顺序码,占3位。顺序码表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
最后1位校验码的生成算法如下所示:
- 第一步:对前17位数字本体码进行加权求和, 记为S;
S = sum(a[i] * w[i]), i = 0, 1, ..., 16
a[i]: 表示第i位置上的身份证号码数字值(0..9)
w[i]: 表示第i位置上的加权因子
w = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
- 第二步:对S以11为底进行取模,记为M;
M = mod(S, 11)
- 第三步:查校验码对照表L求出M对应的校验码。
校验码对照表L = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'] 身份证校验码 = L[M]
02 - Python实现
1 #!/usr/bin/python3 2 """ A simple utility to validate ID for Chinese 3 4 References: 5 o https://jingyan.baidu.com/article/ff4116259e0a7112e48237b9.html 6 o https://www.cnblogs.com/xudong-bupt/p/3293838.html 7 """ 8 9 import sys 10 11 12 def get_verification_code(id17): 13 l_wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] 14 l_vc = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'] 15 s = 0 16 for i in range(len(l_wi)): 17 s += l_wi[i] * int(id17[i]) 18 m = s % 11 19 return l_vc[m] 20 21 22 def is_valid_id(cn_id): 23 cn_id_17 = cn_id[0:-1] 24 vc = get_verification_code(cn_id_17) 25 if vc != cn_id[-1]: 26 print("Oops, invalid ID #<<< should be %s%s" % (cn_id_17, vc), 27 file=sys.stderr) 28 return False 29 return True 30 31 32 def main(argc, argv): 33 if argc != 2: 34 print("Usage: %s <ID>" % argv[0], file=sys.stderr) 35 print("e.g.", file=sys.stderr) 36 print(" %s 51023519890708911X" % argv[0], file=sys.stderr) 37 return 1 38 39 cn_id = argv[1] 40 if len(cn_id) != 18: 41 print("-FAIL, invalid ID as its length is not 18", file=sys.stderr) 42 return 1 43 44 if not is_valid_id(cn_id): 45 print("-FAIL", file=sys.stderr) 46 return 1 47 print("+OK") 48 return 0 49 50 51 if __name__ == '__main__': 52 sys.exit(main(len(sys.argv), sys.argv))
运行样例:
huanli@idorax16:~$ ./validate_id.py 510235198907089112 Oops, invalid ID #<<< should be 51023519890708911X -FAIL huanli@idorax16:~$ ./validate_id.py 51023519890708911X +OK
参考资料:
附录:关于18位身份证设计缺陷的思考
- 最后一位引入罗马数字X属于非典型白痴设计。虽然对质数11取模而不是对10取模从数学上讲是合理的,但如果采用更合理的校验算法去保证校验位仍旧是阿拉伯数字不是更好么?或者使用A代替X,因为A是16进制的10,更适合计算机存储和做数值比较;
- 将身份证有效期印在反面属于典型的未考虑人因工程学设计。复印时浪费纸张姑且可以不谈,因为可以将正反面复印到同一张纸上,但是浪费油墨是一定的;
- 身份证缺乏UUID设计,说明当年的身份证设计者既缺乏计算机思维也缺乏用户隐私保护意识。
>>> 精彩评论来自参考资料2, 截图如下: