共计 1898 个字符,预计需要花费 5 分钟才能阅读完成。
在上一节中,我们学习了正则表达式中的分组与反向引用,掌握了如何捕获和引用特定模式的内容。今天我们将继续深入正则表达式,学习一种更高级但非常实用的技巧—— 零宽断言(lookaround)。
零宽断言的特点是:它匹配一个位置,而不是实际的字符。它用来判断某个位置前后是否存在特定的模式,但不会消耗字符。
一、零宽断言的四种形式
零宽断言主要分为四种:
-
正向前瞻(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)表示单词后必须紧跟数字,但数字本身不计入匹配结果。 -
负向前瞻(Negative Lookahead):
(?!pattern)
表示“当前位置之后不能匹配 pattern”。
例如匹配后面不是数字的单词:pattern = r"\b\w+(?!\d)" result = re.findall(pattern, text) print(result) # 输出 ['item']解释:这里排除了
item1、item2、item3。 -
正向后顾(Positive Lookbehind):
(?<=pattern)
表示“当前位置之前必须能匹配 pattern”。
例如提取冒号后面的内容:text = "user:Tom age:25 city:Shanghai" pattern = r"(?<=:)\w+" result = re.findall(pattern, text) print(result) # 输出 ['Tom', '25', 'Shanghai']解释:
(?<=:)表示匹配冒号后的位置,提取冒号后的单词。 -
负向后顾(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']
这里 (?=@) 仅检查 @ 是否存在,而不会包含在结果中。
四、注意事项
- Python 的
re模块要求后顾(?<=、?<!)中的表达式长度必须固定。
例如(?<=\w{2})a是合法的,但(?<=\w+)a会报错。 - 零宽断言匹配的是“位置”,而不是“内容”,理解这一点非常关键。
- 在复杂文本提取中,可以结合命名分组和零宽断言提高可读性。
五、实际应用示例
假设我们要提取 Markdown 文件中的图片链接(如 ):
text = " some text "
pattern = r"(?<=!\[.*?\]\().*?(?=\))"
result = re.findall(pattern, text)
print(result) # 输出 ['python.png', 'icon.jpg']
通过前瞻与后顾,我们精准地提取出了括号中的文件名,而不包含外围符号。
六、小结
零宽断言是正则表达式中非常强大的工具:
(?=pattern):正向前瞻(?!pattern):负向前瞻(?<=pattern):正向后顾(?<!pattern):负向后顾
掌握它们之后,你可以构建更灵活、更精确的匹配规则,而不破坏原有字符串的结构。