【Python3.9.13 正则问题】请问\d+在字符串尾端为何会产生不同的匹配

s = “hello123world456www7.89你好001”

Match1


match = re.findall(r'\d+(\D+)', s)

['world', 'www', '.', '你好']

Match2


match = re.findall(r'\d+(\D+)\d+', s)

['world', '.']

Match3


match = re.findall(r'\d+(\D+)\d', s)

['world', 'www', '你好']

请问各位大大为啥 \d+在字符串末尾的时候会产生不同的匹配呀,难道是这个d代表除了0~9之外其他字符吗?谢谢解答哟

好神奇, 给人的感觉是从前往后匹配的时候, 如果前面匹配到了 那匹配到的字符就不参加后面的匹配了

比如说Match2中, 第一个匹配到的world 完整的匹配应该是“123world456”, 匹配后面的字符串的时候"123world456"被排除了(也就是后续匹配的时候只有“www7.89你好001”参加了匹配),所以没有匹配到“www”这个结果

我说的这个“被排除了”, 可能更合适的表达应该是 下次匹配从“123world456”之后开始进行,所以就没有包括“456”这三个数字,,不知道表达清楚了没有。。。

后面的也是类似的。。

之前用py好像没有关注到类似的情况…

先确定这几个概念:

  1. \d+ 表示匹配数字
  2. \D+ 表示匹配到非数字字符
  3. re.findall 表示返回 s 里所有匹配到括号里的项,也就是非数字的内容

好了,我们来解题:

  1. Match1: 必须返回前面有数字,后面是字符的内容。这样第一个 hello 前面没数字,后续 world, www, 你好都有数字在前面,最后一个数字 001 后没有字符,所以返回正确

  2. Match2: 必须返回前面有数字,中间是字符,后面还跟着数字的内容。这样第一个 hello 前面没数字但后面有,不正确,world 前面有123,后面又456,所以匹配到,然后 www 因为前面的 456 已经被 world 部分匹配到了,所以算前面没数字,略过,7.89 正好符合前后有数字,中间的点不是数字的情况,匹配到了,再后面的“你好”因为前面的89被点匹配过,即使后面有数字也不满足前面有数字的情况,所以只能抱歉,最终结果是 ‘world’, ‘.’,结果也正确

  3. Match3: 前后必须有数字,但最后一个数字因为没有数量标记,所以必须只有1个。我们再来看看:hello 前面没数字,略过,world 前面有 123,后面 456 里找到了4 (只匹配一个),剩下56用于后续匹配,于是很开森的, www 前面有 56,后面有 7,匹配到了,再往后看 .89 前面没数字,略过,因为没匹配到所以顺延到“你好”字符上,这个字符前面有89,后面有0,很好又匹配到了,最后的01匹配不到,所以最后结果是 ‘world’, ‘www’, '你好‘,结果还是正确

正则表达式写之前脑子里一定要先过一遍,匹配过的不会介入下一次匹配,你用的是 findall,你要用 match 或 search 则只匹配一次,行为又不一样了。

你的疑问是 \d+ 在字符串末尾产生了不同的结果,以为它有多个匹配行为,但实际上从头到尾 \d 都表示匹配数字, + 则表示最少匹配一次或多次,原则一直没变,变的是匹配一次后余下的字符是动态的,你的脑子里没一轮轮的去过滤匹配字符串,一直都拿原始字符串去过滤,所以看到结果摸不到头脑

3 个赞

python 正则匹配即丢弃。可以用断言

match = re.findall(r'(?<=\d)(\D+)(?=\d)', s)

另外 re 的断言限宽。因此如果是其他案例(例如\d+abcd这类)需要安装 regex 模块

2 个赞
r'\d+(?:\D+)'
['123world', '456www', '7.', '89你好']
r'\d+(?:\D+)\d+'
['123world456', '7.89']
r'\d+(?:\D+)\d'
['123world4', '56www7', '89你好0']

用非捕获组,就能清晰的看到为什么了