ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

3种语言身份证号校验完整示例:别在正则上卡半天

3种语言身份证号校验完整示例:别在正则上卡半天 3种语言身份证号校验完整示例:别在正则上卡半天 配置环境就卡半天,改个校验逻辑还要查半天文档?别闹了。 做后端或者前端,身份证号校验是绕不开的坎。很多人上来就写正则,结果发现 GB 11643-1999 标准里的校验位算法、出生日期合法性、地区码有效性,光靠一个正则根本搞不定。今天直接把 Python、JavaScript、Java 三套主流语言的 完整示例 拍在桌上,代码可直接复制运行,原理讲透,坑提前踩好。 1. 三种方案定位:谁适合谁 先说结论,别选错工具。 Python 适合快速原型、数据清洗、爬虫脚本。它的 re 模块和字符串切片足够处理绝大多数校验场景,开发效率最高。缺点?生产环境性能一般,不适合高并发网关。 JavaScript 是前端标配。用户填完表单,身份证号校验必须在浏览器端第一时间拦截,不然请求打过去被后端拒了,体验极差。JS 方案要兼顾兼容性和体积,不能引入重型库。 Java 是后端服务主力。Spring Boot 里做参数校验,或者微服务网关统一拦截,Java 方案要线程安全、可集成 Bean Validation。性能要求高时,缓存校验结果能省不少 CPU。 三者的核心差异,看这张表:维度 Python JavaScript Java执行环境 服务端脚本、数据管道 浏览器、Node.js JVM、微服务、网关校验位计算 原生整型,无溢出风险 需用 BigInt 或拆位计算 原生 long,需注意 18 位末位是 X日期校验 datetime 模块直观 Date 对象有坑,推荐手动比对 LocalDate 线程安全,推荐地区码校验 需自行维护映射表 前端可预置精简表 可集成数据库或 Redis 缓存正则支持 re 模块强大 引擎差异大,建议手动实现 Pattern 编译复用,性能好典型场景 数据清洗、ETL、快速验证 表单前置校验、小程序 服务端权威校验、API 网关2. 核心原理:别只背正则 很多人以为 身份证号校验 就是套个正则,错。真正的校验分三层: 第一层:格式校验。18 位,前 17 位数字,末位数字或 X。地区码 6 位,出生日期 8 位,顺序码 3 位,校验码 1 位。 第二层:逻辑校验。出生日期必须是真实存在的日期,不能是 2025 年 13 月 40 号。顺序码第 17 位是性别,奇数男,偶数女。 第三层:校验位校验。这是最容易被忽略的。前 17 位数字分别乘以权重因子 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2,求和后对 11 取模,余数对应校验码映射表:1,0,X,9,8,7,6,5,4,3,2。 根据 GB 11643-1999《公民身份号码》 国家标准,以及 公安部开发者文档 中关于人口信息校验的技术规范,这三层缺一不可。只查格式,假号一抓一大把;加上校验位,错误率能降 90% 以上。 3. 代码写法对比:三套完整示例 Python 版本:简洁直接 import re from datetime import datetimedef validate_id_card(id_card: str) - bool:# 1. 格式校验:18位,前17数字,末位数字或Xif not re.match(r'^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$', id_card):return False# 2. 日期校验birth_str = id_card[6:14]try:datetime.strptime(birth_str, %Y%m%d)except ValueError:return False# 3. 校验位校验weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]check_map = '10X98765432'total = sum(int(id_card[i]) * weights[i] for i in range(17))expected = check_map[total % 11]return id_card[-1].upper() == expected逐行讲解:正则里 ^ 和 $ 锁定首尾,防止前后混入空格。 (18|19|20)\d{2} 限制年份范围,避免 1700 年或 2100 年。 (0[1-9]|1[0-2]) 月份 1-12,(0[1-9]|[12]\d|3[01]) 日期 1-31,再配合 datetime.strptime 二次确认,杜绝 02 月 30 日。 校验位计算用列表推导式,一行搞定,Python 的优势就在这里。JavaScript 版本:前端友好 function validateIdCard(idCard) {if (!/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(idCard)) {return false;}const birthStr = idCard.substring(6, 14);const year = parseInt(birthStr.substring(0, 4));const month = parseInt(birthStr.substring(4, 6));const day = parseInt(birthStr.substring(6, 8));// 手动日期校验,避免 Date 对象时区坑if (month 1 || month 12 || day 1 || day 31) return false;const daysInMonth = new Date(year, month, 0).getDate();if (day daysInMonth) return false;const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];const checkMap = '10X98765432';let sum = 0;for (let i = 0; i 17; i++) {sum += parseInt(idCard[i]) * weights[i];}return idCard[17].toUpperCase() === checkMap[sum % 11]; }避坑点:别用 new Date(birthStr)。不同浏览器对 YYYYMMDD 解析不一致,有的直接返回 Invalid Date。手动拆年月日,用 new Date(year, month, 0).getDate() 获取当月天数,最稳。 parseInt 前确保是数字,否则 NaN 会污染求和。 前端校验只是前置拦截,绝不能替代后端校验。用户能改 DOM,能抓包,能直接调 API。Java 版本:后端权威 import java.time.LocalDate; import java.util.regex.Pattern;public class IdCardValidator {private static final Pattern ID_PATTERN = Pattern.compile(^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]$);private static final int[] WEIGHTS = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};private static final String CHECK_MAP = 10X98765432;public static boolean validate(String idCard) {if (idCard == null || !ID_PATTERN.matcher(idCard).matches()) {return false;}String birthStr = idCard.substring(6, 14);int year = Integer.parseInt(birthStr.substring(0, 4));int month = Integer.parseInt(birthStr.substring(4, 6));int day = Integer.parseInt(birthStr.substring(6, 8));try {LocalDate.of(year, month, day);} catch (Exception e) {return false;}int sum = 0;for (int i = 0; i 17; i++) {sum += (idCard.charAt(i) - '0') * WEIGHTS[i];}return String.valueOf(idCard.charAt(17)).equalsIgnoreCase(String.valueOf(CHECK_MAP.charAt(sum % 11)));} }性能优化:Pattern 是线程安全的,static final 编译一次,多次复用,避免每次 matcher 重新编译。 LocalDate.of 比 SimpleDateFormat 线程安全且快,Java 8+ 项目首选。 高并发场景下,可对 idCard - boolean 结果加一层 Caffeine 或 Redis 缓存,重复查询直接命中。4. 适用场景与选型建议 前端表单:选 JavaScript。用户体验第一,输入失焦即校验,红色提示框比请求失败强一百倍。代码控制在 50 行内,不要引入 lodash 或 dayjs 这种重型依赖,就为校验一个 ID。 数据清洗与 ETL:选 Python。几百万条历史数据,要过滤掉假号、错号,Python 的 pandas 配合上面的校验函数,apply 一下,半小时跑完。Java 写这个纯属脱裤子放屁。 后端 API 与微服务网关:选 Java。Spring Boot 里集成 Bean Validation,自定义 @ValidIdCard 注解,所有入口统一拦截。Java 的 Pattern 编译复用,QPS 上万也不虚。如果校验逻辑复杂,比如要关联地区码、性别、年龄限制,Java 的面向对象优势就出来了,可以扩展成 IdCardInfo 对象,包含解析后的出生日期、性别、地区。 Node.js 后端:用 JavaScript 版本,但要注意 BigInt。如果未来要处理 18 位以上扩展编码,或者做批量求和,Number 精度可能不够,提前用 BigInt 封装。 避坑清单:末位 X 的大小写:用户可能输入小写 x,必须 toUpperCase 或 equalsIgnoreCase 处理。 出生日期跨世纪:1999 年、2000 年、2100 年,正则里 (18|19|20) 要覆盖。 地区码 0 开头:正则首位 [1-9] 已经排除,但内部 6 位地区码允许 0,别写死成 \d{5}。 不要信任前端:前端校验只是 UX,后端必须再校验一遍。安全是后端的事,体验是前端的事,别混。 日志脱敏:打印日志时,身份证号中间 8 位必须打码,110101********1234,否则违反《个人信息保护法》,出事就是大事。5. 进阶:地区码与性能优化 校验位只是基础,真正复杂的业务要查地区码。6 位地区码前 2 位是省,中间 2 位是市,最后 2 位是区县。可以维护一个精简的映射表,比如: {110000: 北京市,310000: 上海市,440000: 广东省 }前端预置 Top 100 地区,覆盖 95% 用户;后端查数据库或 Redis,全量地区码。如果业务需要精确到区县,建议用 GeoHash 或行政区划编码库,别自己硬编码。 性能上,身份证号校验 的 CPU 开销主要在正则匹配和字符串操作。高并发下,可以用 Bloom Filter 预过滤明显错误的格式,再走完整校验。或者,把校验结果缓存 24 小时,同一个 ID 短期内重复请求,直接返回缓存。 还有,别用正则校验日期。正则能匹配 20231345,但 datetime 或 LocalDate 知道 13 月不存在。正则负责格式,原生日期类负责逻辑,各司其职。 结语 身份证号校验 不是背正则,是理解 GB 11643-1999 标准里的校验位算法,是分清前端拦截和后端权威的边界,是知道 Python 适合跑数据、Java 适合扛并发、JavaScript 适合保体验。 代码都给你了,完整示例 可以直接拷走用。配置环境还卡?大概率是依赖版本问题,Python 查 requirements.txt,Java 查 pom.xml,JS 查 package.json,别把校验逻辑的锅甩给环境。 还有什么不懂的?评论区留言挨个回。是正则匹配不上?是校验位算错?还是前端跨域问题?直接说,看到就答。
返回列表