Python基础入门 Day111 正则表达式中的零宽断言(前瞻与后顾)

47次阅读
没有评论

共计 1898 个字符,预计需要花费 5 分钟才能阅读完成。

在上一节中,我们学习了正则表达式中的分组与反向引用,掌握了如何捕获和引用特定模式的内容。今天我们将继续深入正则表达式,学习一种更高级但非常实用的技巧—— 零宽断言(lookaround)

零宽断言的特点是:它匹配一个位置,而不是实际的字符。它用来判断某个位置前后是否存在特定的模式,但不会消耗字符。

一、零宽断言的四种形式
零宽断言主要分为四种:

  1. 正向前瞻(Positive Lookahead)(?=pattern)
    表示“当前位置之后必须能匹配 pattern”。
    例如匹配后面跟着数字的单词:

    import re
    
    text = "item1 item2 item item3"
    pattern = r"\b\w+(?=\d)"
    result = re.findall(pattern, text)
    print(result)  # 输出 ['item', 'item', 'item']
    

    解释:(?=\d) 表示单词后必须紧跟数字,但数字本身不计入匹配结果。

  2. 负向前瞻(Negative Lookahead)(?!pattern)
    表示“当前位置之后不能匹配 pattern”。
    例如匹配后面不是数字的单词:

    pattern = r"\b\w+(?!\d)"
    result = re.findall(pattern, text)
    print(result)  # 输出 ['item']
    

    解释:这里排除了 item1item2item3

  3. 正向后顾(Positive Lookbehind)(?<=pattern)
    表示“当前位置之前必须能匹配 pattern”。
    例如提取冒号后面的内容:

    text = "user:Tom age:25 city:Shanghai"
    pattern = r"(?<=:)\w+"
    result = re.findall(pattern, text)
    print(result)  # 输出 ['Tom', '25', 'Shanghai']
    

    解释:(?<=:) 表示匹配冒号后的位置,提取冒号后的单词。

  4. 负向后顾(Negative Lookbehind)(?<!pattern)
    表示“当前位置之前不能匹配 pattern”。
    例如匹配不以冒号开头的单词:

    pattern = r"(?<!:)\b\w+\b"
    result = re.findall(pattern, text)
    print(result)  # 输出 ['user', 'age', 'city']
    

    解释:这些单词前面没有冒号,因此被匹配。

二、前瞻与后顾的组合
前瞻和后顾可以组合使用,以实现更精确的匹配。例如,匹配在冒号后并且在空格前的内容:

pattern = r"(?<=:)\w+(?=\s)"
text = "name:Tom age:25"
result = re.findall(pattern, text)
print(result)  # 输出 ['Tom', '25']

解释:

  • (?<=:) 表示左边必须有冒号
  • (?=\s) 表示右边必须是空格
  • 中间的 \w+ 被提取出来

三、零宽断言的优势
零宽断言最大的优势是:

  • 它不会消耗字符
  • 可以让匹配规则更灵活
  • 能在不改变捕获内容的前提下控制上下文

例如匹配邮箱的用户名部分:

text = "[email protected] [email protected]"
pattern = r"\b\w+(?=@)"
result = re.findall(pattern, text)
print(result)  # 输出 ['abc', 'def']

这里 (?=@) 仅检查 @ 是否存在,而不会包含在结果中。

四、注意事项

  1. Python 的 re 模块要求后顾(?<=?<!)中的表达式长度必须固定。
    例如 (?<=\w{2})a 是合法的,但 (?<=\w+)a 会报错。
  2. 零宽断言匹配的是“位置”,而不是“内容”,理解这一点非常关键。
  3. 在复杂文本提取中,可以结合命名分组和零宽断言提高可读性。

五、实际应用示例
假设我们要提取 Markdown 文件中的图片链接(如 ![alt](image.png)):

text = "![logo](python.png) some text ![icon](icon.jpg)"
pattern = r"(?<=!\[.*?\]\().*?(?=\))"
result = re.findall(pattern, text)
print(result)  # 输出 ['python.png', 'icon.jpg']

通过前瞻与后顾,我们精准地提取出了括号中的文件名,而不包含外围符号。

六、小结
零宽断言是正则表达式中非常强大的工具:

  • (?=pattern):正向前瞻
  • (?!pattern):负向前瞻
  • (?<=pattern):正向后顾
  • (?<!pattern):负向后顾

掌握它们之后,你可以构建更灵活、更精确的匹配规则,而不破坏原有字符串的结构。

正文完
 0
评论(没有评论)