IPv6 校验的简化思路
IPv6 地址是八组十六进制数,用冒号分隔,并用双冒号表示一段连续的零。写一条覆盖全部情况的巨型正则既难读懂也难维护,稳妥做法是把校验拆成可验证的几步:先剥离区域标识与前缀长度,再看有没有双冒号,最后数段落数量与每段位数。
基本信息
| 基本形态 | 最多八组,每组 1 到 4 位十六进制,组间用冒号分隔 |
|---|---|
| 双冒号 | 表示一段连续的零,整条地址里最多出现一次,且至少代表一组 |
| 嵌入形式 | 末尾可写 IPv4,例如以 ::ffff: 开头的映射地址 |
| 非地址部分 | 区域标识以百分号开头(如百分号加网卡名)、前缀长度以斜杠加数字结尾,都不属于地址本身 |
| 大小写 | 十六进制数字大小写均可,比较与存储时建议统一小写 |
典型使用场景
- 配置录入校验:填 IPv6 监听地址、上游地址时先挡住形状错误
- 解析日志:从访问日志中识别地址并按前缀聚合,注意日志里常带区域标识
- 地址规范化:把缩写与全写统一成一种形式便于比较与去重
- 网段判断:先校验地址形状,再按前缀长度与地址做包含判断
- 入库前清洗:剥离前缀长度与端口,避免把非地址字符写进字段
分步校验写法
分步校验写法
# 第一步:剥掉非地址部分,例如区域标识与前缀长度
addr = raw.split('%')[0].split('/')[0] # 区域标识与掩码不属于地址
# 第二步:用字符集限定合法字符,再做结构判断
if not re.fullmatch(r'[0-9A-Fa-f:]{2,45}', addr): reject()
# 第三步:按是否含双冒号分别数段落
parts = addr.split('::') # 双冒号拆分后应不超过两段
if len(parts) > 2: reject() # 双冒号最多出现一次
# 第四步:交给地址库做权威判断,正则只做前置过滤
import ipaddress; ipaddress.IPv6Address(addr) # 抛出异常即为非法常见形态与判断要点
| 形态 | 特征 | 判断要点 |
|---|---|---|
| 完整八组 | 含七个冒号 | 每组 1 到 4 位十六进制 |
| 含双冒号 | 压缩了一段或多段零 | 两侧组数之和不超过七 |
| 映射地址 | 末尾直接写 IPv4 | 需要单独允许含点号的形式 |
| 带区域标识 | 地址后跟百分号与网卡名 | 先剥离再校验 |
| 带前缀长度 | 结尾斜杠加数字 | 先在斜杠处截断 |
RFC 5952 给出了推荐书写形式:每组去前导零、连续零段压缩为双冒号、字母统一小写。建议在入库前做一次规范化,否则同一地址的多种写法会被当成不同值,影响去重与统计。
地址形状合法不代表可用:环回、链路本地、唯一本地等前缀各有用途,是否可分配要按场景判断;带区域标识的地址只在本地链路有意义,做跨机比较前必须先去掉区域部分。