什么是零宽断言
断言(Assertion)是一种特殊的正则结构,它匹配的是位置而不是字符——就像 \b 和 ^ 一样,它不"消耗"任何字符。"零宽"(Zero-width)正是指它匹配空字符串(宽度为零)。
零宽断言分为四种:
| 语法 | 类型 | 含义 |
|---|---|---|
| (?=...) | 正向前瞻 | 后面紧跟着 ... 时匹配 |
| (?!...) | 负向前瞻 | 后面不跟 ... 时匹配 |
| (?<=...) | 正向后顾 | 前面紧跟着 ... 时匹配 |
| (?<!...) | 负向后顾 | 前面不跟 ... 时匹配 |
正向前瞻 (?=...)
"在满足某条件的位置匹配"。前瞻不包含在最终匹配结果中:
/\d+(?= dollars)/g
价格是 100 dollars,不是 200 euros
只匹配后面跟着 " dollars" 的数字,但 " dollars" 本身不在结果中
# 提取单位前的数字 re.findall(r'\d+(?=\s*(?:元|块|RMB))', '价格 99元,优惠价 88 块') # → ['99', '88']
负向前瞻 (?!...)
"不在某条件的位置匹配":
/\bfoo(?!bar)\b/g
foo foobar foobaz foolish
匹配不后跟 "bar" 的 "foo"("foolish" 中的 foo 也匹配,因为后面是 "lish")
# 匹配不在引号内的逗号(简化示例) re.findall(r',(?![^"]*")', text) # 密码验证:包含数字但不是纯数字 re.match(r'^(?=.*\d)(?!^\d+$).{8,}$', password)
正向后顾 (?<=...)
"在某内容之后的位置匹配"。后顾同样不包含在最终结果中:
/(?<=\$)\d+/g
价格:$100,优惠:$80
只匹配 $ 符号后面的数字,结果不含 $
# 提取 HTML 属性值(简化) re.findall(r'(?<=href=")[^"]+', html) # 在特定前缀后提取内容 re.search(r'(?<=version:\s)\S+', 'version: 3.2.1').group() # → '3.2.1'
WARNING大多数引擎要求后顾断言是固定长度的(不能包含
*/+/?)。Python 的 re 模块有此限制;Python 3.x 的 regex 第三方库支持可变长度后顾。JavaScript 从 ES2018 起支持后顾断言,且支持可变长度。负向后顾 (?
"不在某内容之后的位置匹配":
# 匹配不在小数点后的数字 re.findall(r'(?<!\.\d)\d+', '价格 100 元,折扣 0.85') # 匹配不以 "un" 开头的单词 re.findall(r'(?<!un)\w+', text)
断言的组合使用
多个断言可以叠加在同一位置,实现复杂条件:
# 密码强度:6位以上,含大写字母,含数字,含特殊字符 pattern = r'^(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%]).{6,}$' def check_password(pwd): return bool(re.match(pattern, pwd)) check_password('Abc123!') # True check_password('abc123!') # False(无大写) check_password('ABC123') # False(无特殊字符)
零宽断言 vs 捕获组
| 捕获组 () | 零宽断言 (?=...) | |
|---|---|---|
| 匹配字符 | 是,包含在结果中 | 否,只检查位置 |
| 消耗字符 | 是 | 否 |
| 可用量词 | 是((abc)+) | 否(断言不可重复) |
实用示例
# 千位分隔符:在数字中插入逗号 re.sub(r'(?<=\d)(?=(\d{3})+$)', ',', '1234567890') # → '1,234,567,890' # 提取引号内的内容(不含引号) re.findall(r'(?<=")\w+(?=")', '"hello" and "world"') # → ['hello', 'world'] # 找出 TODO 注释中的任务 re.findall(r'(?<=TODO:\s).+', code)
小结
- 零宽断言匹配位置,不消耗字符,不出现在匹配结果中
(?=...)正向前瞻:后面紧跟某模式时匹配(?!...)负向前瞻:后面不跟某模式时匹配(?<=...)正向后顾:前面紧跟某模式时匹配(多数引擎限定固定长度)(?<!...)负向后顾:前面不跟某模式时匹配- 多个断言可以叠加,实现密码强度等复杂验证