IPv4 地址正则表达式
IPv4 地址由四个十进制数字组成,每个数字的范围是 0 到 255,数字之间用点()分隔,168.1.1。

简单但不精确的版本
一个最简单的正则表达式来匹配 "点分十进制" 格式是:
import re
pattern = r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
问题所在:这个表达式会匹配 999.999.999 这样的非法 IP 地址,因为它只检查了数字和点的格式,没有验证每个数字的范围(0-255)。
精确的版本(推荐)
为了精确匹配合法的 IPv4 地址,我们需要确保每个 8 位组(octet)都在 0 到 255 之间,这需要一个更复杂的正则表达式。
核心思路:

- 匹配 0-199:
1\d{2}(匹配 100-199),[1-9]\d(匹配 10-99),[0-9](匹配 0-9),可以合并为(?:1\d{2}|[1-9]\d|\d)。 - 匹配 200-249:
2[0-4]\d。 - 匹配 250-255:
25[0-5]。 - 将以上三种情况合并,得到匹配 0-255 的表达式:
(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)。 - 用
\.匹配点,并将这个整体重复四次,用\.连接起来。
最终的正则表达式:
import re
# 使用非捕获组 (?:...) 来提高效率,避免捕获不必要的子组
ipv4_pattern = r"""
(?:25[0-5]| # 匹配 250-255
2[0-4][0-9]| # 匹配 200-249
1[0-9]{2}| # 匹配 100-199
[1-9][0-9]| # 匹配 10-99
[0-9]) # 匹配 0-9
\. # 匹配一个点
{3} # 重复前面的模式3次(因为有3个点)
(?:25[0-5]| # 匹配最后一个 0-255 的数字
2[0-4][0-9]|
1[0-9]{2}|
[1-9][0-9]|
[0-9])
"""
使用示例:
import re
# 编译正则表达式,re.VERBOSE 允许我们写注释和换行,使表达式更易读
ipv4_regex = re.compile(ipv4_pattern, re.VERBOSE)
# 测试用例
valid_ips = [
"192.168.1.1",
"10.0.0.1",
"127.0.0.1",
"0.0.0.0",
"255.255.255.255",
"8.8.8.8"
]
invalid_ips = [
"256.1.1.1", # 数字超出范围
"192.168.01.1",# 前导零(虽然有些系统允许,但标准正则通常不匹配)
"192.168.1", # 缺少一位
"192.168.1.1.1",# 多了一位
"a.b.c.d", # 非数字字符
" 192.168.1.1 " # 包含空格
]
print("--- 验证有效的 IPv4 地址 ---")
for ip in valid_ips:
if ipv4_regex.fullmatch(ip):
print(f"'{ip}' 是一个有效的 IPv4 地址。")
else:
print(f"'{ip}' 不是一个有效的 IPv4 地址。 (意外!)")
print("\n--- 验证无效的 IPv4 地址 ---")
for ip in invalid_ips:
if not ipv4_regex.fullmatch(ip):
print(f"'{ip}' 不是一个有效的 IPv4 地址。")
else:
print(f"'{ip}' 是一个有效的 IPv4 地址。 (意外!)")
关键点:
fullmatch():确保整个字符串都匹配 IP 地址格式,避免从长字符串中提取出 IP 地址,用fullmatch("192.168.1.1")会成功,而fullmatch("my ip is 192.168.1.1")会失败。re.VERBOSE:这是一个强大的标志,允许你在正则表达式中添加空格和注释,极大地提高了复杂正则的可读性。
IPv6 地址正则表达式
IPv6 地址比 IPv4 复杂得多,它由 8 组 4 位的十六进制数表示,组之间用冒号()分隔。2001:0db8:85a3:0000:0000:8a2e:0370:7334。

核心规则
- 每组是 1 到 4 个十六进制字符(不区分大小写)。
- 可以有连续的零组,可以用双冒号 进行压缩( 在一个地址中只能出现一次)。
- 为了方便,IPv4 地址可以嵌入在 IPv6 地址的最后 32 位,形成 IPv4 映射地址,
:ffff:192.168.1.1。
精确的版本(非常复杂)
匹配所有合法的 IPv6 地址(包括压缩格式和 IPv4 映射)是一个巨大的挑战,下面是一个公认的比较全面的正则表达式。
正则表达式:
import re
# 这个正则表达式非常复杂,但能处理大多数标准 IPv6 格式
ipv6_pattern = r"""
^
# 处理 :: 开头的情况,::1
(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|
# 处理 :: 结尾的情况,2001::
(?:(?:[0-9A-Fa-f]{1,4}:){1,7}:)|
# 处理 :: 在中间的情况,2001::1
(?:(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4})|
# 处理 :: 在中间,且前面有多个组的情况
(?:(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2})|
(?:(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3})|
(?:(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4})|
(?:(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5})|
# 处理 :: 在最前面,后面有多个组的情况
[0-9A-Fa-f]{1,4}:(?::[0-9A-Fa-f]{1,4}){1,6}|
# 处理单独的 ::,表示全0
:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|
# 处理 IPv4 映射地址
(?:(?:[0-9A-Fa-f]{1,4}:){1,7}:)| # ::ffff:192.168.1.1
(?:IPv4的精确正则表达式) # 这个部分比较复杂,下面会简化
)
$
"""
简化版的 IPv6 正则(推荐用于多数场景)
由于上述表达式过于复杂,在实际应用中,一个简化但能覆盖大部分情况的版本可能更实用,这个版本不处理 的所有变体,但能匹配标准格式和 IPv4 映射。
import re
# 简化的 IPv6 正则表达式
# 它匹配标准的 8 组格式,以及 :: 开头/中间的情况,但不穷举所有组合
# 它也匹配 IPv4 映射地址
simplified_ipv6_pattern = r"""
^
# 标准格式 8组
(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|
# 包含 :: 的格式
(?:(?:[0-9A-Fa-f]{1,4}:){1,7}:)|
(?:(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4})|
(?:(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2})|
(?:(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3})|
(?:(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4})|
(?:(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5})|
[0-9A-Fa-f]{1,4}:(?::[0-9A-Fa-f]{1,4}){1,6}|
:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|
# IPv4 映射地址
(?:[0-9A-Fa-f]{1,4}:){1,7}:IPv4精确正则表达式
$
"""
更推荐的实践:使用 ipaddress 模块
由于 IPv6 正则表达式极其复杂且容易出错,Python 官方推荐使用 ipaddress 模块来处理 IP 地址,因为它更可靠、更安全,并且能处理所有边缘情况。
import ipaddress
def is_valid_ip(ip_str):
try:
# 尝试创建一个 IP 地址对象
# ip_str 是有效的 IPv4 或 IPv6 地址,它会成功
ipaddress.ip_address(ip_str)
return True
except ValueError:
# 如果不是有效的 IP 地址,会抛出 ValueError
return False
# 测试
print(is_valid_ip("192.168.1.1")) # True
print(is_valid_ip("2001:0db8::1")) # True
print(is_valid_ip("::ffff:192.168.1.1")) # True
print(is_valid_ip("256.1.1.1")) # False
print(is_valid_ip("not.an.ip")) # False
同时匹配 IPv4 和 IPv6
如果你需要一个正则表达式来同时匹配 IPv4 和 IPv6 地址,你可以将两者的正则表达式用 (或) 连接起来。
import re
# 使用前面定义的精确 ipv4_pattern 和简化 ipv6_pattern
# 注意:这里为了清晰,重新写了一遍
ipv4_pattern = r"(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)"
# 这是一个非常基础的 IPv6 匹配,不处理 :: 压缩
# 实际项目中,强烈建议使用 ipaddress 模块
ipv6_pattern = r"(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}"
# 组合起来
ip_pattern = f"({ipv4_pattern}|{ipv6_pattern})"
ip_regex = re.compile(ip_pattern)
text = "我的服务器地址是 192.168.1.1,另一个是 2001:0db8:85a3:0000:0000:8a2e:0370:7334。"
# findall 会返回所有匹配到的 IP 地址
found_ips = ip_regex.findall(text)
print("在文本中找到的 IP 地址是:")
for ip in found_ips:
print(ip)
总结与最佳实践
| 方法 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| 正则表达式 (IPv4) | 速度快,适合简单的格式验证。 | 容易出错,难以覆盖所有规则(如前导零)。 | 需要在文本中快速查找或提取 IPv4 地址时。 |
| 正则表达式 (IPv6) | 理论上可以匹配所有格式。 | 极其复杂,难以编写和维护,容易遗漏边缘情况。 | 不推荐,除非有特殊需求,否则应避免。 |
ipaddress 模块 |
强烈推荐,官方标准,准确、可靠、安全,能处理所有合法/非法 IP 格式。 | 相比正则,代码量稍多一点,但可读性更好。 | 所有涉及 IP 地址验证、解析、比较、转换的场景。 |
最终建议:
- 如果你只是想验证一个字符串是否是合法的 IPv4 地址,使用精确的 IPv4 正则表达式和
fullmatch()是一个不错的选择。 - 如果你需要处理 IPv6 地址,请立即放弃正则表达式,直接使用 Python 内置的
ipaddress模块,它能为你省去无数的麻烦和潜在的 bug。 - 如果你需要从一个大的文本块中同时提取 IPv4 和 IPv6 地址,可以组合使用简化的正则表达式,但请务必清楚其局限性,并对结果进行二次验证(用
ipaddress模块)。
