身份证校验必须严格按四步:先验长度为18位且仅含数字或末位x;再验前17位全数字、日期合法(含闰年判断);然后按国标加权求和模11;最后对照余数表(0→'1',1→'0',10→'x')验证校验码。

身份证号长度和字符类型校验必须放在第一步
18位是硬性门槛,少一位或多一位直接淘汰;同时必须全是数字加末尾可能的X(大小写都得接受)。很多人只检查长度,结果让"11010119900307295a"这种含非法字母的串混过第一关。
实操建议:
- 先用
str.length() != 18快速过滤 - 再遍历前17位:每个字符必须满足
c >= '0' && c - 第18位只能是数字或
'X'或'x',别漏掉小写 - 别用
std::isdigit()——它依赖 locale,中文环境可能返回 false
地区码(前6位)要查表,不能只看是否数字
前6位是行政区划代码,比如 "110101" 是北京市东城区,但 "119999" 就不存在。网上能搜到最新《GB/T 2260》代码表,但实际项目里没必要加载完整 CSV,可取交集:用常见省市代码建一个 std::unordered_set<:string></:string>,覆盖 99% 场景足够。
常见错误:
- 把
"000000"当成合法地区码(实际不存在) - 接受已撤销区划如
"320104"(原南京鼓楼区,2013年已合并)——若业务对历史数据不敏感,可放宽 - 忽略新设区划,比如
"310118"(上海市青浦区,2022年升格为市辖区)
出生日期(第7–14位)必须能解析且真实存在
这部分是 YYYYMMDD 格式,但光格式对不行:得是真实日期,还要考虑闰年、大小月、以及“2月30日”这种明显错值。
实操建议:
- 用
std::stoi()提取年、月、日,别用std::sscanf或手撕子串——容易越界 - 年份范围建议卡在
1900–2099(公安部标准是 1900–2099,太早或太晚大概率造假) - 月份必须是
1–12,日期需按月校验:if (month == 2 && day > 29) → false,别忘了闰年判断 - 注意:
"20000229"合法(闰年),但"19000229"不合法(1900 年不是闰年)
最后一位校验码必须按国标算法算出来
前17位加权求和模11,对照余数查校验码表——这是唯一不可绕过的数学验证。很多人抄网上的“加权系数表”,但常犯两个错:系数顺序反了,或者余数映射错了(比如把余数 10 对应成 '10' 而不是 'X')。
关键细节:
- 权重数组必须是
{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2},从左到右对应第1–17位 - 余数
0–10映射到{'1','0','X','9','8','7','6','5','4','3','2'}——注意0→'1',1→'0',10→'X' - 计算时记得把字符转数字:
str[i] - '0',别直接用 ASCII 值 - 大小写
X都要转成大写再比,避免"x"和'X'比较失败
真正容易被忽略的是:校验码算法对前17位的每一位都敏感,哪怕改一个数字,结果就全错。所以只要这里挂了,前面所有检查都白做。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











