一、题目理解
IPv4地址格式要求:
- 必须是 4段数字 ,用点(.) 分隔
- 每段必须是 0-255 之间的整数
- 不能有前导零(比如"01"、"001"无效,但单个"0"有效)
- 每段必须是纯数字,不能有其他字符
有效示例:
"192.168.1.1""10.0.0.1""0.0.0.0""255.255.255.255"
无效示例:
"192.168.1"(只有3段)"192.168.1.1.1"(5段)"192.168.001.1"(001有前导零)"256.0.0.1"(256超过255)"192.168.1.a"(包含字母)"192.168..1"(有空段)
二、解法一:模拟法(逐步验证)
这是最直观的方法,按步骤验证每个条件。
完整代码:
python
def is_valid_ipv4_simulation(ip: str) -> bool:
"""
使用模拟法验证IPv4地址
"""
# 1. 按点号分割字符串
parts = ip.split('.')
# 2. 验证是否有4段
if len(parts) != 4:
return False
# 3. 验证每一段
for part in parts:
# 3.1 验证是否为纯数字
if not part.isdigit():
return False
# 3.2 验证长度不超过3
if len(part) > 3:
return False
# 3.3 将字符串转为整数
num = int(part)
# 3.4 验证范围0-255
if num < 0 or num > 255:
return False
# 3.5 验证没有前导零(除非就是0)
if len(part) > 1 and part[0] == '0':
return False
# 4. 所有验证通过
return True
逐行详细解释:
第1步:分割字符串
python
parts = ip.split('.')
split('.'):按点号分割字符串- 例如:
"192.168.1.1"→["192", "168", "1", "1"]
第2步:验证段数
python
if len(parts) != 4:
return False
- IPv4必须是4段
- 注意:
split('.')会把连续的点号变成空字符串"192.168..1"→["192", "168", "", "1"](长度为4但有空段)
第3步:验证每一段
3.1 验证是否为纯数字
python
if not part.isdigit():
return False
isdigit():判断字符串是否只包含数字字符- 可以过滤掉:空字符串
""、字母、符号等 - 注意:
""不是数字,所以会返回False
3.2 验证长度不超过3
python
if len(part) > 3:
return False
- 最大数字是255,长度最多3位
3.3 转为整数
python
num = int(part)
- 将数字字符串转为整数
- 注意:前面已经验证过
isdigit(),所以这里不会出错
3.4 验证范围0-255
python
if num < 0 or num > 255:
return False
- IPv4每段范围是0-255
- 注意:0是允许的(比如
"0.0.0.0")
3.5 验证没有前导零
python
if len(part) > 1 and part[0] == '0':
return False
len(part) > 1:长度大于1(不是单个数字)part[0] == '0':第一个字符是0- 这样会排除:
"01"、"001"、"012"等 - 但允许:
"0"(单个0是合法的)
测试验证:
python
# 测试用例
test_cases = [
("192.168.1.1", True),
("0.0.0.0", True),
("255.255.255.255", True),
("10.0.0.1", True),
("192.168.01.1", False), # 前导零
("192.168.1", False), # 只有3段
("192.168.1.1.1", False), # 5段
("256.0.0.1", False), # 超过255
("192.168.1.a", False), # 包含字母
("192.168..1", False), # 有空段
("-1.0.0.1", False), # 负数
]
for ip, expected in test_cases:
result = is_valid_ipv4_simulation(ip)
print(f"ip: {ip:20} expected: {expected} result: {result} {'✓' if result == expected else '✗'}")
三、解法二:正则表达式法
正则表达式可以一次性验证所有条件,代码更简洁。
先理解IPv4的正则规则
IPv4的每段数字可以分解为:
- 0-9(1位数字,可以是0-9)
- 10-99(2位数字,第一位1-9,第二位0-9)
- 100-199(3位数字,第一位1,第二位0-9,第三位0-9)
- 200-249(3位数字,第一位2,第二位0-4,第三位0-9)
- 250-255(3位数字,第一位2,第二位5,第三位0-5)
把这些情况用正则表达式写出来:
正则表达式分解学习:
1. 先看单段的正则:
regex
(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])
分解:
25[0-5]:匹配 250-2552[0-4][0-9]:匹配 200-2491[0-9][0-9]:匹配 100-199[1-9][0-9]:匹配 10-99(第一位1-9,第二位0-9)[0-9]:匹配 0-9(允许0,但不允许前导0,因为前面已经有[1-9][0-9])
注意:这个正则已经自动排除了前导零,因为:
- 一位数可以是0-9
- 两位数第一位必须是1-9(不能是0)
- 三位数第一位必须是1或2(也不能是0)
2. 完整的IPv4正则:
regex
^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$
分解:
^:字符串开始( ... \.){3}:重复3次(三段数字+点号)( ... ):第4段数字(没有点号)$:字符串结束
完整代码(正则版):
python
import re
def is_valid_ipv4_regex(ip: str) -> bool:
"""
使用正则表达式验证IPv4地址
"""
# 编译正则表达式
pattern = re.compile(r'^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$')
# 匹配整个字符串
return bool(pattern.match(ip))
测试正则版:
python
# 使用相同的测试用例
for ip, expected in test_cases:
result = is_valid_ipv4_regex(ip)
print(f"ip: {ip:20} expected: {expected} result: {result} {'✓' if result == expected else '✗'}")
四、正则表达式详细讲解(给初学者的)
如果你对正则表达式不熟悉,让我详细解释每个部分:
正则表达式基础语法:
^:匹配字符串开始$:匹配字符串结束|:或(选择)():分组[]:字符集合,匹配其中任意一个字符{n}:重复n次\.:点号(.在正则中有特殊含义,需要转义)
分部分理解:
1. 匹配250-255:
regex
25[0-5]
25:匹配字符"25"[0-5]:匹配0-5中任意一个数字- 组合:250、251、252、253、254、255
2. 匹配200-249:
regex
2[0-4][0-9]
2:匹配字符"2"[0-4]:匹配0-4中任意一个数字[0-9]:匹配0-9中任意一个数字- 组合:200-249
3. 匹配100-199:
regex
1[0-9][0-9]
1:匹配字符"1"- 两个
[0-9]:各匹配0-9中任意一个数字 - 组合:100-199
4. 匹配10-99:
regex
[1-9][0-9]
[1-9]:匹配1-9中任意一个数字(不能是0)[0-9]:匹配0-9中任意一个数字- 组合:10-99
5. 匹配0-9:
regex
[0-9]
[0-9]:匹配0-9中任意一个数字- 组合:0-9
为什么这个正则能排除前导零?
看看10-99的情况:
- 第一位是
[1-9],不能是0 - 所以"01"、"02"等不会被匹配
三位数的情况:
- 100-199:第一位是1,不是0
- 200-249:第一位是2,不是0
- 250-255:前两位是25,不是0
这样,"001"、"012"等都不会被匹配。
五、两种方法的比较
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 模拟法 | 逻辑清晰,易于理解和调试 | 代码较长,多个if语句 | 面试中展示逻辑思维,适合初学者 |
| 正则法 | 代码简洁,一行搞定 | 正则表达式难写难懂,调试困难 | 实际项目中,追求代码简洁 |
六、面试回答建议
如果面试官问这道题:
- 先讲思路:"我会用点号分割字符串,然后验证4个条件..."
- 写代码:用模拟法写,边写边解释
- 优化:"其实也可以用正则表达式,正则表达式更简洁,但可读性稍差"
- 测试:给出几个测试用例
示例回答:
"这道题验证IPv4地址,主要看4点:1.是否4段;2.每段是否纯数字;3.是否在0-255;4.是否没有前导零。
我会先用split按点分割,然后逐一验证。比如先看长度是否为4,然后每段用isdigit判断是否为数字,
再转成整数看范围,最后检查长度大于1时首位不能是0。
也可以用正则表达式,比如^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}...$
但正则可能不太直观。"
七、力扣实战
现在请你:
- 题目链接:468. 验证IP地址
- 测试所有示例
- 尝试自己写一个测试函数,随机生成IP测试
IPv6地址格式要求 :
必须是 8段数字,用点(.) 分隔
每段长度1-4个字符
合法字符:0,1,2,...,9,a,b,c,d,e,f,A,B,C,D,E,F
允许前导零
bash
class Solution:
def validIPAddress(self, queryIP: str) -> str:
# 情况1:检查是否是IPv4
if '.' in queryIP:
parts = queryIP.split('.')
# 必须正好有4段
if len(parts) != 4:
return "Neither"
for part in parts:
# 1. 每段不能为空
if not part:
return "Neither"
# 2. 每段必须都是数字
if not part.isdigit():
return "Neither"
# 3. 检查数字范围 (0-255)
num = int(part)
if num < 0 or num > 255:
return "Neither"
# 4. 检查前导零:如果长度>1且第一个字符是'0',无效
if len(part) > 1 and part[0] == '0':
return "Neither"
# 所有检查通过
return "IPv4"
# 情况2:检查是否是IPv6
elif ':' in queryIP:
parts = queryIP.split(':')
# 必须正好有8段
if len(parts) != 8:
return "Neither"
# 定义合法的十六进制字符
hex_digits = set("0123456789abcdefABCDEF")
for part in parts:
# 1. 每段长度必须在1-4之间
if len(part) < 1 or len(part) > 4:
return "Neither"
# 2. 每个字符必须是十六进制字符
for char in part:
if char not in hex_digits:
return "Neither"
# 所有检查通过
return "IPv6"
# 情况3:既不是IPv4也不是IPv6
else:
return "Neither"
随机测试代码:
python
import random
def generate_random_ip():
"""生成随机IP地址(可能无效)"""
parts = []
for _ in range(4):
# 随机生成数字或字母
if random.random() < 0.8: # 80%概率生成数字
num = random.randint(0, 300) # 可能超过255
parts.append(str(num))
else: # 20%概率生成字母
parts.append(chr(random.randint(97, 122))) # a-z
return ".".join(parts)
# 测试随机IP
for i in range(10):
ip = generate_random_ip()
result1 = is_valid_ipv4_simulation(ip)
result2 = is_valid_ipv4_regex(ip)
if result1 != result2:
print(f"不一致: {ip} 模拟法:{result1} 正则法:{result2}")
八、常见陷阱和注意事项
- 空字符串 :
split('.')可能产生空字符串 - 负数 :
isdigit()对于负数返回False(因为"-"不是数字) - 前导零的判断:要小心"0"是合法的
- 大数字:虽然长度超过3可以直接排除,但转int前最好先判断长度
- 边界值:0和255都是有效的
记住:面试时模拟法更安全,因为你可以边写边解释思路。正则表达式虽然简洁,但如果写错或解释不清,可能扣分。