谈谈regex (?:) (?=) (?!) (?<=) (?<!)
一、简介(?:pattern)、(?=pattern)、 (?!pattern)、 (?<=pattern)、 (?<!pattern)

这是我做的一张思维导图,说一个容易被忽视的地方,就是零宽断言会匹配出任何满足pattern的位置,而不是像捕获组一样下次匹配是在上次匹配记录之后开始的。
例如:
import re s = "1234567890" pattern1 = "(\d{2})" # 捕获组匹配 print(re.findall(pattern1, s)) # ['12', '34', '56', '78', '90'] """ care: 捕获组匹配下次匹配是从上次匹配结果后开始匹配 而不是['12', '23', '34',...] """ print(re.sub(pattern1, ",", s)) # , , , , , pattern2 = "(?=\d{2})" # 非捕获组匹配 print(re.findall(pattern2, s)) # ['', '', '', '', '', '', '', '', ''] """ care: 匹配的是"\d{2}"这个模式的头位置, 而不包含"\d{2}"这个模式 """ print(re.sub(pattern2, ",", s)) # , 1, 2, 3, 4, 5, 6, 7, 8, 90 """ care: 12满足\d{2}这个模式,匹配到12之前的这个位置,所以此时替换后结果为, 1234567890 23满足\d{2}这个模式,匹配到23之前的这个位置, 所以此时替换后结果为, 1, 234567890 依次类推... 到最后一个数字0是不满足\d{2}这个模式了,所以最后一个替换出现在90这一次, 1, 2, 3, 4, 5, 6, 7, 8, 90 """
二、快速理解零宽断言
举个栗子,有以下文本:
1 我要吃好吃的~我要下班~我要睡觉 2 我不要吃好吃的~我要下班~我要睡觉 3 我要吃好吃的~我要下班3~我不要睡觉 伪代码: positive_lookahead_regex = 我要下班~(?=我要睡觉)
//这里的含义为 匹配"我要下班~"这个模式,然后匹配我要睡觉之前的头位置,最后的匹配结果只是"我要下班~"。 negative_lookahead_regex = 我要下班3~(?!我要睡觉)
// 这里的含义为 匹配"我要下班3"这个模式,然后匹配 不满足"我要睡觉"这个模式的头位置。 最后的匹配结果只是"我要下班3"。 positive_lookbehind_regex = (?<=我要吃好吃的~)我要下班 negative_lookbehind_regex = (?<!我要吃好吃的~)我要下班
快速记忆方法:
1、首先(? 开头 表示非捕获组
2、然后如果是负向的断言 则会跟 "<"
3、最后 肯定 是"=",否定是"!"
Q: 正向 肯定 预查 equals (?=pattern)
负向 否定 预查 equals (?<!pattern)
四、实践中不断理解
str1 = "123456789 12384" pattern = \B(?=(?:\d{3})+(?!\d)) Q:如果用这个模式去 匹配str1,并且对匹配到的位置进行替换成"," 最终结果会是什么,这个过程是怎样的?
最好自己有答案,在看结果哦~
str1 = "123456789 12384" pattern = "/\B(?=(?:\d{3})+(?!\d))/g" str1.replace(pattern,',') // "123,456,789 12,384"

浙公网安备 33010602011771号