前言
之前写sql 时遇到一个问题,要匹配以房间号结尾的地址模式字符串,但是输入的数据中存在以路号结尾数据,单纯的采用’/d+号$',会匹配到路号或者楼号的模式。想到可以用正则中的先行断言和后行断言实现,故在这里对它们进行梳理,并记录实现时遇到的后行断言不支持变长的错误。
先行断言和后行断言
正则表达式的先行断言和后行断言一共有 4 种形式:
- (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion)
- 表达式一(?=子组表达式)表达式二:先捕获’表达式一表达式二’,满足的话,再判断表达式二是否以子组表达式开头,即对表达式二开头有肯定断言。
- (?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)
- 表达式一(?!子组表达式)表达式二:先捕获’表达式一表达式二’,满足的话,再判断表达式二是否是不以子组表达式开头,即对表达式二开头有否定断言
- (?<=pattern) 零宽正向后行断言(zero-width positive lookbehind assertion)
- 表达式一(?<=子组表达式)表达式二:先捕获’表达式一表达式二’,再看表达式一的结尾是否符合子组表达式肯定断言
- (?<!pattern) 零宽负向后行断言(zero-width negative lookbehind assertion)
- 表达式一(?<!子组表达式)表达式二:先捕获’表达式一表达式二’,再看表达式一的结尾是否符合子组表达式否定断言
实例:
- 正向先行断言
re.search(r'abc(?=d.*f).*f',"fishabc@abcdefade") # 识别"abcdef" 无法识别 "abc@abcdef"
re.search(r'.*[路|街|道](?=.?[0-9]+号).*',"风景大街第100号商铺") # 识别到***风景大街第100号商铺***
## PS:当表达式二的前面是.*时,表达式一可以放子组表达式开头,即表示对表达式二开头有正向断言
- 负向先行断言
re.search(r'abc(?!d.*f).*f',"fishabcd@abcdefade") # 无法识别***abcdef***; 识别"abc@abcdef"
re.search(r'.*[路|街|道](?!.?[0-9]+号).*号',"风景大街第100-101号商铺") # 无法识别到***风景大街第100-101号商铺***
## PS:当表达式一为空或.*时,表达式二应该有有一个确定性的开头,否则子组表达式的否定通常都能成立
re.search(r'(?!风景大街10000号).*号',"风景大街10000号") # 可以识别"景大街10000号"
- 正向后行断言
re.search(r'abc.*a(?<=fa)',"fishabc@abcdefade") # 识别到***abc@abcdefa***
re.search(r'.*[路|街|道].?[0-9]+号(?<=[0-9]号).*',"风景大街第100号商铺") # 识别到***风景大街第100号商铺***
## PS:在python 实现是不支持子组表达式是变长,如re.search(r'.*[路|街|道].?[0-9]+号(?<=[0-9]+号).*',"风景大街第100号商铺") 将报错。
- 负向后行断言
re.search(r'abc.*a(?<!fa)',"fishabc@abcdefade") # 识别到***abc@a***
re.search(r'.*[路|街|道].?[0-9]+(?<![0-9]号).*',"风景大街第100-号商铺") # 识别到***风景大街第100-号商铺***
## PS:同正向后行断言,该断言在python 实现是不支持子组表达式是变长,
结论
先行断言支持对模式开头进行断言,在进行负向断言时表达式二的开头不能是任意字符;后行断言对模式结尾进行断言,在python实现后行断言时断言的子组表达式不能是变长的模式。
一些问题难以解决可能不是问题本身很难,而是没有对问题进行更好的描述。前文中问题可以描述为,识别以’号’结尾的文本且对该文本断言不是以路加路号结尾(负向后行断言);或识别不以’路’开头的中文+数字+'号’结尾的模式串(负向先行断言)