欢迎光临
我们一直在努力

(#字符串处理)判断字符串是否为有效IPv4地址

一、题目理解

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"(有空段)

    二、解法一:模拟法(逐步验证)

    这是最直观的方法,按步骤验证每个条件。

    完整代码:

    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步:分割字符串

    parts = ip.split('.')

    • split('.'):按点号分割字符串
    • 例如:"192.168.1.1" → ["192", "168", "1", "1"]
    第2步:验证段数

    if len(parts) != 4:
    return False

    • IPv4必须是4段
    • 注意:split('.')会把连续的点号变成空字符串
      • "192.168..1" → ["192", "168", "", "1"](长度为4但有空段)
    第3步:验证每一段
    3.1 验证是否为纯数字

    if not part.isdigit():
    return False

    • isdigit():判断字符串是否只包含数字字符
    • 可以过滤掉:空字符串""、字母、符号等
    • 注意:""不是数字,所以会返回False
    3.2 验证长度不超过3

    if len(part) > 3:
    return False

    • 最大数字是255,长度最多3位
    3.3 转为整数

    num = int(part)

    • 将数字字符串转为整数
    • 注意:前面已经验证过isdigit(),所以这里不会出错
    3.4 验证范围0-255

    if num < 0 or num > 255:
    return False

    • IPv4每段范围是0-255
    • 注意:0是允许的(比如"0.0.0.0")
    3.5 验证没有前导零

    if len(part) > 1 and part[0] == '0':
    return False

    • len(part) > 1:长度大于1(不是单个数字)
    • part[0] == '0':第一个字符是0
    • 这样会排除:"01"、"001"、"012"等
    • 但允许:"0"(单个0是合法的)

    测试验证:

    # 测试用例
    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. 先看单段的正则:

    (25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])

    分解:

    • 25[0-5]:匹配 250-255
    • 2[0-4][0-9]:匹配 200-249
    • 1[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正则:

    ^((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段数字(没有点号)
    • $:字符串结束

    完整代码(正则版):

    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))

    测试正则版:

    # 使用相同的测试用例
    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:

    25[0-5]

    • 25:匹配字符"25"
    • [0-5]:匹配0-5中任意一个数字
    • 组合:250、251、252、253、254、255
    2. 匹配200-249:

    2[0-4][0-9]

    • 2:匹配字符"2"
    • [0-4]:匹配0-4中任意一个数字
    • [0-9]:匹配0-9中任意一个数字
    • 组合:200-249
    3. 匹配100-199:

    1[0-9][0-9]

    • 1:匹配字符"1"
    • 两个[0-9]:各匹配0-9中任意一个数字
    • 组合:100-199
    4. 匹配10-99:

    [1-9][0-9]

    • [1-9]:匹配1-9中任意一个数字(不能是0)
    • [0-9]:匹配0-9中任意一个数字
    • 组合:10-99
    5. 匹配0-9:

    [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 允许前导零

    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"

    随机测试代码:

    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都是有效的

  • 记住:面试时模拟法更安全,因为你可以边写边解释思路。正则表达式虽然简洁,但如果写错或解释不清,可能扣分。

    赞(0)
    未经允许不得转载:171主机测评 » (#字符串处理)判断字符串是否为有效IPv4地址
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址