资讯动态

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

发布时间:2026/8/14 21:29:57 来源:尧图企业网站定制
文章目录环境信息前言正则能匹配格式但匹配不了真假一、香港身份证号码的格式三层结构二、校验码算法mod 11 加权三、香港车牌格式传统 vs 自定义四、香港电话号码格式五、完整验证器可直接复用六、三个易错点坑1全角括号坑2字母转数字的偏移量坑3逻辑有效 ≠ 真实存在七、总结环境信息项目版本/说明Python3.10标准库re正则、无需第三方依赖验证对象香港身份证 / 车牌 / 电话号码数据来源格式规则和校验码算法为公开标准前言正则能匹配格式但匹配不了真假上周我在做一个表单数据清洗的活需要校验用户填写的香港身份证号码。我第一反应是写个正则表达式importre patternr^[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_PATTERNre.compile(r^([A-Z]{1,2})(\d{6})[(]([0-9A])[)]$)defparse_hkid(hkid:str):解析香港身份证号码返回(字母部分, 数字部分, 校验码)mHKID_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(AB9876543))# 全角括号也能识别print(parse_hkid(A1234563))# 无括号 → None不匹配注意一个细节我用了[(]和[)]来同时匹配半角和全角括号——香港用户在中文输入法下输入时经常打出全角括号只匹配半角会漏掉一大批真实数据。二、校验码算法mod 11 加权这是本文的核心。香港身份证的校验码算法是公开的逻辑如下字母转数字A10, B11, C12, …, Z35单字母补空格如果只有一个开头字母前面补一个空格值36加权相乘从右到左权重依次是 9,8,7,6,5,4,3,2,1校验码位权重是1求和取余所有乘积相加除以 11 取余数校验码规则余数 0 → 校验码是 0余数 1 → 校验码是 A余数 2~10 → 校验码 11 - 余数用A123456(3)举例验证A10单字母前面补空格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位值序列不含校验码位# 字母转数字A10 ... Z35values[]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]totalsum(v*wforv,winzip(values,weights))# 4. 取余remaindertotal%11# 5. 校验码规则ifremainder0:return0elifremainder1:returnAelse:returnstr(11-remainder)defvalidate_hkid(hkid:str)-bool:完整验证香港身份证号码格式 校验码parsedparse_hkid(hkid)ifnotparsed:returnFalseletters,digits,check_digitparsed calculatedcalculate_hkid_check_digit(letters,digits)returncalculatedcheck_digit# 测试print(calculate_hkid_check_digit(A,123456))# 3print(validate_hkid(A123456(3)))# Trueprint(validate_hkid(A123456(9)))# False校验码不对收藏提示②validate_hkid这个函数是两层验证的完整实现——第一层正则查格式第二层 mod 11 查校验码。直接用validate_hkid()就能判断一个香港身份证号码是否逻辑有效。注意逻辑有效 ≠ 真实存在它只能证明号码结构正确不能证明这个身份证真的发给了某个人。三、香港车牌格式传统 vs 自定义香港车牌有两种格式完全不同传统车牌1-2 个字母 1-4 位数字如AB 1234、A 1。自定义车牌2006年起最多 8 个字符可以是字母数字空格的任意组合如MYCAR、L0VE U。# 传统车牌1-2字母 1-4数字可能有空格分隔TRADITIONAL_PLATEre.compile(r^([A-Z]{1,2})\s?(\d{1,4})$)# 自定义车牌最多8个字符字母/数字/空格CUSTOM_PLATEre.compile(r^[A-Z0-9 ]{1,8}$)defparse_plate(plate:str)-str:识别香港车牌类型plateplate.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_PATTERNre.compile(r^(?:\852|00852)?\s?([2-9]\d{7})$)defparse_phone(phone:str)-str:解析香港电话号码mPHONE_PATTERN.match(phone.strip())ifnotm:return无效电话号码numberm.group(1)prefixnumber[0]ifprefixin23:returnf固话:{number}elifprefixin4569:returnf手机:{number}else:returnf其他:{number}print(parse_phone(852 2345 6789))# 固话print(parse_phone(98765432))# 手机9开头print(parse_phone(12345678))# 无效1开头且只有7位数字五、完整验证器可直接复用把三个验证器整合成一个工具类importreclassHKDataValidator:香港身份证/车牌/电话格式验证器# 身份证HKID_PATTERNre.compile(r^([A-Z]{1,2})(\d{6})[(]([0-9A])[)]$)# 车牌TRADITIONAL_PLATEre.compile(r^([A-Z]{1,2})\s?(\d{1,4})$)CUSTOM_PLATEre.compile(r^[A-Z0-9 ]{1,8}$)# 电话PHONE_PATTERNre.compile(r^(?:\852|00852)?\s?([2-9]\d{7})$)staticmethoddef_letter_to_num(ch:str)-int:returnord(ch)-ord(A)10classmethoddefvalidate_hkid(cls,hkid:str)-bool:mcls.HKID_PATTERN.match(hkid.strip().upper())ifnotm:returnFalseletters,digits,check_digitm.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]totalsum(v*wforv,winzip(values,weights))remaindertotal%11# 校验码ifremainder0:calculated0elifremainder1:calculatedAelse:calculatedstr(11-remainder)returncalculatedcheck_digitclassmethoddefvalidate_plate(cls,plate:str)-bool:plateplate.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()))# 使用示例validatorHKDataValidator()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) 1A 会变成 1整个校验码全错。正确是 10# ❌ 错误A1# ✅ 正确A10def_letter_to_num(ch):returnord(ch)-ord(A)10坑3逻辑有效 ≠ 真实存在校验码算法只能证明这个号码的结构是自洽的校验码和前面的字符对得上。它不能证明这个身份证真的发给了某个人。真正的身份核验需要调政府接口或人工核对正则和校验码只是第一道防线。收藏提示③三个坑里全角括号和字母偏移是最容易在真实项目里踩的。字母 A10 不是 A1——这个偏移量错了整个校验码算法就崩了。七、总结正则表达式能解决的是格式对不对。校验码算法能解决的是号码逻辑真不真。香港身份证号码恰好是两者的完美结合——格式1-2字母6数字校验码适合正则校验码mod 11 加权适合算法验证。三个东西记住正则验证格式校验码验证逻辑——两层验证缺一不可这是数据校验的通用思路香港身份证 A10 不是 A1——字母转数字的偏移量是最容易写错的细节全角括号是真实数据里的坑——中文输入法下半角正则匹配不到全角括号这篇文章是0810那篇数据清洗的自然延续——0810讲的是怎么处理Big5编码这篇讲的是怎么校验数据格式。两者合起来才是完整的香港数据清洗与验证闭环。本文为 Python 正则表达式与校验码算法的技术分享。香港身份证校验码算法为公开标准mod 11 加权文中示例号码均为演示用途的假号码不涉及任何真实个人数据。本文不构成任何身份核验建议。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价