当前位置: 首页 > news >正文

Python正则表达式实战:解析和验证香港身份证、车牌、电话格式

文章目录

    • 环境信息
    • 前言:正则能匹配"格式",但匹配不了"真假"
    • 一、香港身份证号码的格式:三层结构
    • 二、校验码算法:mod 11 加权
    • 三、香港车牌格式:传统 vs 自定义
    • 四、香港电话号码格式
    • 五、完整验证器(可直接复用)
    • 六、三个易错点
      • 坑1:全角括号
      • 坑2:字母转数字的偏移量
      • 坑3:逻辑有效 ≠ 真实存在
    • 七、总结

环境信息

项目版本/说明
Python3.10+
标准库re(正则)、无需第三方依赖
验证对象香港身份证 / 车牌 / 电话号码
数据来源格式规则和校验码算法为公开标准

前言:正则能匹配"格式",但匹配不了"真假"

上周我在做一个表单数据清洗的活,需要校验用户填写的香港身份证号码。我第一反应是写个正则表达式:

importre pattern=r'^[A-Z]{1,2}\d{6}\([0-9A]\)$'

格式上是匹配了——1到2个大写字母 + 6位数字 + 括号里的校验码。但很快我就发现一个问题:A123456(3)A123456(9)都符合这个正则,但只有一个是真的。

正则表达式只能验证"格式对不对",验证不了"号码是不是真实存在的"。香港身份证号码里那个括号中的校验码,不是随便写的——它是通过一个 mod 11 加权算法算出来的。格式匹配只是第一层,校验码验证才是第二层。

这篇文章讲的就是这"两层验证"。前半部分是正则解析香港身份证、车牌、电话的格式,后半部分是香港身份证校验码算法的完整实现。

收藏提示①:正则匹配"像不像",校验码算法验证"是不是真的"。表单验证、数据清洗、反作弊,缺一层都不行。文末有完整可复用的验证器代码。

一、香港身份证号码的格式:三层结构

香港身份证号码由三部分组成(以A123456(3)为例):

A 123456 (3) ↑ ↑ ↑ 字母 6位数字 校验码(0-9或A)

规则:

  • 开头是 1-2 个大写英文字母(A-Z)
  • 中间是 6 位数字
  • 括号里是校验码,0-9 或字母 A
  • 括号可能是半角()或全角()

对应的正则:

importre# 支持半角/全角括号,校验码0-9或AHKID_PATTERN=re.compile(r'^([A-Z]{1,2})(\d{6})[((]([0-9A])[))]$')defparse_hkid(hkid:str):"""解析香港身份证号码,返回(字母部分, 数字部分, 校验码)"""m=HKID_PATTERN.match(hkid.strip().upper())ifnotm:returnNonereturnm.group(1),m.group(2),m.group(3)# 测试print(parse_hkid('A123456(3)'))# ('A', '123456', '3')print(parse_hkid('AB987654(3)'))# 全角括号也能识别print(parse_hkid('A1234563'))# 无括号 → None(不匹配)

注意一个细节:我用了[((][))]来同时匹配半角和全角括号——香港用户在中文输入法下输入时,经常打出全角括号,只匹配半角会漏掉一大批真实数据。

二、校验码算法:mod 11 加权

这是本文的核心。香港身份证的校验码算法是公开的,逻辑如下:

  1. 字母转数字:A=10, B=11, C=12, …, Z=35
  2. 单字母补空格:如果只有一个开头字母,前面补一个"空格",值=36
  3. 加权相乘:从右到左,权重依次是 9,8,7,6,5,4,3,2,1(校验码位权重是1)
  4. 求和取余:所有乘积相加,除以 11 取余数
  5. 校验码规则
    • 余数 = 0 → 校验码是 0
    • 余数 = 1 → 校验码是 A
    • 余数 = 2~10 → 校验码 = 11 - 余数

A123456(3)举例验证:

A=10,单字母前面补空格=36 加权:36×9 + 10×8 + 1×7 + 2×6 + 3×5 + 4×4 + 5×3 + 6×2 = 324 + 80 + 7 + 12 + 15 + 16 + 15 + 12 = 481 481 ÷ 11 = 43 余 8 11 - 8 = 3 → 校验码 = 3 ✓

完整实现:

defcalculate_hkid_check_digit(letters:str,digits:str)->str:""" 计算香港身份证号码的校验码 letters: 开头字母部分(1-2个大写字母) digits: 6位数字字符串 返回: 校验码('0'-'9' 或 'A') """# 1. 构建完整的8位"值序列"(不含校验码位)# 字母转数字:A=10 ... Z=35values=[]iflen(letters)==1:values.append(36)# 单字母时,前面补空格=36values.append(ord(letters[0])-ord('A')+10)else:# 双字母values.append(ord(letters[0])-ord('A')+10)values.append(ord(letters[1])-ord('A')+10)# 2. 数字部分forchindigits:values.append(int(ch))# 3. 加权求和(权重从9开始递减)weights=[9,8,7,6,5,4,3,2]total=sum(v*wforv,winzip(values,weights))# 4. 取余remainder=total%11# 5. 校验码规则ifremainder==0:return'0'elifremainder==1:return'A'else:returnstr(11-remainder)defvalidate_hkid(hkid:str)->bool:"""完整验证香港身份证号码(格式 + 校验码)"""parsed=parse_hkid(hkid)ifnotparsed:returnFalseletters,digits,check_digit=parsed calculated=calculate_hkid_check_digit(letters,digits)returncalculated==check_digit# 测试print(calculate_hkid_check_digit('A','123456'))# '3'print(validate_hkid('A123456(3)'))# Trueprint(validate_hkid('A123456(9)'))# False(校验码不对)

收藏提示②:validate_hkid这个函数是"两层验证"的完整实现——第一层正则查格式,第二层 mod 11 查校验码。直接用validate_hkid()就能判断一个香港身份证号码是否逻辑有效。注意:逻辑有效 ≠ 真实存在(它只能证明号码结构正确,不能证明这个身份证真的发给了某个人)。

三、香港车牌格式:传统 vs 自定义

香港车牌有两种,格式完全不同:

传统车牌:1-2 个字母 + 1-4 位数字,如AB 1234A 1

自定义车牌(2006年起):最多 8 个字符,可以是字母+数字+空格的任意组合,如MYCARL0VE U

# 传统车牌:1-2字母 + 1-4数字(可能有空格分隔)TRADITIONAL_PLATE=re.compile(r'^([A-Z]{1,2})\s?(\d{1,4})$')# 自定义车牌:最多8个字符(字母/数字/空格)CUSTOM_PLATE=re.compile(r'^[A-Z0-9 ]{1,8}$')defparse_plate(plate:str)->str:"""识别香港车牌类型"""plate=plate.strip().upper()ifTRADITIONAL_PLATE.match(plate):returnf'传统车牌:{plate}'elifCUSTOM_PLATE.match(plate):returnf'自定义车牌:{plate}'return'无效车牌'print(parse_plate('AB 1234'))# 传统车牌print(parse_plate('MYCAR'))# 自定义车牌print(parse_plate('ABC12345'))# 无效(9个字符超限)

四、香港电话号码格式

香港电话号码统一 8 位数字,但不同号段有不同含义:

  • 固话:通常 2 或 3 开头
  • 手机:4/5/6/9 开头
  • 国际格式+852前缀
# 8位数字(可带 +852 或 00852 国际前缀)PHONE_PATTERN=re.compile(r'^(?:\+852|00852)?\s?([2-9]\d{7})$')defparse_phone(phone:str)->str:"""解析香港电话号码"""m=PHONE_PATTERN.match(phone.strip())ifnotm:return'无效电话号码'number=m.group(1)prefix=number[0]ifprefixin'23':returnf'固话:{number}'elifprefixin'4569':returnf'手机:{number}'else:returnf'其他:{number}'print(parse_phone('+852 2345 6789'))# 固话print(parse_phone('98765432'))# 手机(9开头)print(parse_phone('12345678'))# 无效(1开头,且只有7位数字)

五、完整验证器(可直接复用)

把三个验证器整合成一个工具类:

importreclassHKDataValidator:"""香港身份证/车牌/电话格式验证器"""# 身份证HKID_PATTERN=re.compile(r'^([A-Z]{1,2})(\d{6})[((]([0-9A])[))]$')# 车牌TRADITIONAL_PLATE=re.compile(r'^([A-Z]{1,2})\s?(\d{1,4})$')CUSTOM_PLATE=re.compile(r'^[A-Z0-9 ]{1,8}$')# 电话PHONE_PATTERN=re.compile(r'^(?:\+852|00852)?\s?([2-9]\d{7})$')@staticmethoddef_letter_to_num(ch:str)->int:returnord(ch)-ord('A')+10@classmethoddefvalidate_hkid(cls,hkid:str)->bool:m=cls.HKID_PATTERN.match(hkid.strip().upper())ifnotm:returnFalseletters,digits,check_digit=m.group(1),m.group(2),m.group(3)# 构建值序列iflen(letters)==1:values=[36,cls._letter_to_num(letters[0])]else:values=[cls._letter_to_num(letters[0]),cls._letter_to_num(letters[1])]values+=[int(ch)forchindigits]# 加权求和weights=[9,8,7,6,5,4,3,2]total=sum(v*wforv,winzip(values,weights))remainder=total%11# 校验码ifremainder==0:calculated='0'elifremainder==1:calculated='A'else:calculated=str(11-remainder)returncalculated==check_digit@classmethoddefvalidate_plate(cls,plate:str)->bool:plate=plate.strip().upper()returnbool(cls.TRADITIONAL_PLATE.match(plate)orcls.CUSTOM_PLATE.match(plate))@classmethoddefvalidate_phone(cls,phone:str)->bool:returnbool(cls.PHONE_PATTERN.match(phone.strip()))# 使用示例validator=HKDataValidator()print(validator.validate_hkid('A123456(3)'))# Trueprint(validator.validate_hkid('A123456(9)'))# Falseprint(validator.validate_plate('AB 1234'))# Trueprint(validator.validate_phone('+852 9876 5432'))# True

六、三个易错点

坑1:全角括号

香港用户用中文输入法时,括号经常是全角()。如果你只写\(...\)(半角),会漏掉一大批真实数据。正则里同时匹配半角和全角,用[((][))]

坑2:字母转数字的偏移量

字母 A 对应数字10(不是 1)。这是最容易写错的地方——如果你用ord(ch) - ord('A') + 1,A 会变成 1,整个校验码全错。正确是+ 10

# ❌ 错误:A=1# ✅ 正确:A=10def_letter_to_num(ch):returnord(ch)-ord('A')+10

坑3:逻辑有效 ≠ 真实存在

校验码算法只能证明"这个号码的结构是自洽的"(校验码和前面的字符对得上)。它不能证明这个身份证真的发给了某个人。真正的身份核验需要调政府接口或人工核对,正则和校验码只是第一道防线。

收藏提示③:三个坑里,全角括号和字母偏移是最容易在真实项目里踩的。字母 A=10 不是 A=1——这个偏移量错了,整个校验码算法就崩了。

七、总结

正则表达式能解决的,是"格式对不对"。校验码算法能解决的,是"号码逻辑真不真"。香港身份证号码恰好是两者的完美结合——格式(1-2字母+6数字+校验码)适合正则,校验码(mod 11 加权)适合算法验证。

三个东西记住:

  1. 正则验证格式,校验码验证逻辑——两层验证缺一不可,这是数据校验的通用思路
  2. 香港身份证 A=10 不是 A=1——字母转数字的偏移量,是最容易写错的细节
  3. 全角括号是真实数据里的坑——中文输入法下,半角正则匹配不到全角括号

这篇文章是0810那篇"数据清洗"的自然延续——0810讲的是怎么处理Big5编码,这篇讲的是怎么校验数据格式。两者合起来,才是完整的"香港数据清洗与验证"闭环。


本文为 Python 正则表达式与校验码算法的技术分享。香港身份证校验码算法为公开标准(mod 11 加权),文中示例号码均为演示用途的假号码,不涉及任何真实个人数据。本文不构成任何身份核验建议。

http://www.cnnetsun.cn/news/4028683.html

相关文章:

  • 材料发现基准测试:大语言模型为半导体寻材有成果,但也存在可合成性等问题!
  • Kodi 字幕下载太折腾?这款免费字幕插件 5 分钟解决找字幕难题
  • 26MHz热敏晶振换料实战:手机无线子板从泰晶切换到鸿星的选型与验证记录
  • Stable Diffusion入门第16-18天:从文生图到图生图——用一张图“导演”你的AI创作
  • AI搜索技术解析:从Gemini模型到工程落地实践
  • 基于多智能体协作的AI绘画:GPT-Image-2 Skill与Hermes框架实战
  • EdgeRemover:彻底移除 Edge 的终极指南
  • AI数字化蛋白筛选到底是否靠谱?AI-PPI/多模态/虚拟筛选一篇汇总!
  • 深入解析vLLM:PagedAttention如何革新LLM推理的显存管理与吞吐量
  • 想做测试工具却不会写代码?怎么办?
  • 文生TikZ
  • 国家工程技术研究中心申报条件有哪些
  • Python Django电商比价系统:从爬虫到智能Agent的全栈实践
  • 智能代码搜索:从向量化到混合检索,揭秘“离谱快”背后的技术架构
  • RTX Spark:在个人PC上搭建GPU加速的Spark大数据与AI开发环境
  • 三步装好Android Studio中文语言包,开发界面5分钟变中文
  • SH9持续同调拓扑正则性与无导数奇点检测:拓扑表示对应等价条件与Navier-Stokes奇异性的拓扑刻画
  • 终极指南:如何使用Holehe进行邮箱账号安全检测
  • Jinq 社区精选:用户最常问的 20 个问题与解答
  • klogg日志分析终极指南:如何快速检索10GB级日志文件(安装、搜索与实时监控全攻略)
  • ElasticSearch Paramedic源码解析:Ember.js前端架构与ElasticSearch API交互原理
  • 微信聊天记录导出其实很简单:换手机前,我把三年对话永久存进了本地
  • 新手必看:R3PLAYX界面导航与基础操作完全指南
  • Minum框架扩展指南:从零开发自定义数据库引擎的完整教程
  • 高阶C++-SFINAE
  • 开源共享记忆服务Lindy:突破AI上下文限制,构建可记忆的智能应用
  • mcrcon 跨平台编译实战:看懂一条裸命令,三端构建零报错
  • Java校园智能车辆管理系统设计与实现
  • 材料告急时,FGO玩家需要的不是一个攻略站
  • 一张内部图来解读UMI AIGC SAAS,优秘智能到底想做什么?