正则 re.findall(r'x?','xy123'),x 重复 0 次是什么意思?

2016-11-06 18:35:16 +08:00
 jin6220
>>> re.findall(r'xy','xy123') #从源文本 xy123 中找 xy 刚好找到一个。
['xy']
>>> re.findall(r'x','xy123') #从源文本 xy123 中找 x 刚好找到一个。
['x']
>>> re.findall(r'x?','xy123')
['x', '', '', '', '', '']

最后这个实在无法理解,
从中找 x 重复 0 次或者 1 次,如果 x 重复 1 次,得到 x,
关键是重复 0 次怎么理解?输出的结果里有 5 个表示空东西的东西,但是源文本 xy123 中没有它啊?!
2462 次点击
所在节点    Python
16 条回复
noli
2016-11-06 18:42:58 +08:00
不要理解为"重复",理解为"匹配"。
匹配零次,就是匹配一个空集合,显然,任何"东西"都不属于一个空集合。所以 x?,就是匹配字母 x 一次,显然这是跟单独一个 x 是一样的效果,这是因为问号后面没别的条件了
noli
2016-11-06 18:47:26 +08:00
@noli 然后再看匹配零次,因为只有空元素才能属于空集合。所以,后面字符串有多少个字符,就匹配了多少次空集合
jin6220
2016-11-06 19:06:13 +08:00
@noli 好吧 不过 0 次这个实在是头疼
正则的作用,我粗略理解是从源文字中寻找符合匹配模式的东西,
x? ,如果?是零次的话,匹配模式是个空集合,但是源文字中不是没有空集合么?
noli
2016-11-06 19:19:40 +08:00
@jin6220 任何集合与空集的交集,结果都是空集。
DiamondbacK
2016-11-06 19:21:13 +08:00
'xy123' 长度为 5 ,所以 re.findall 最多可以在 5 个位置尝试匹配。
第一个位置是在字符串首,匹配到了 'x'。
然后跳过匹配到的字符串,移到 'y' 前面,进行第二次匹配尝试,匹配到 ''。
然后往前移动一个字符,即 'y' 和 '1' 中间,进行第三次匹配尝试,匹配到 ''。
然后往前移动一个字符,即 '1' 和 '2' 中间,进行第四次匹配尝试,匹配到 ''。
然后往前移动一个字符,即 '2' 和 '3' 中间,进行第五次匹配尝试,匹配到 ''。
然后往前移动一个字符,即串尾,结束所有匹配尝试。

任何文本都包含 ''。
DiamondbacK
2016-11-06 19:24:14 +08:00
更正

'xy123' 长度为 5 ,所以 re.findall 最多可以在 5 个位置尝试匹配。
第一个位置是在字符串首,匹配到了 'x'。
然后跳过匹配到的字符串,移到 'y' 前面,进行第二次匹配尝试,匹配到 ''。
然后往前移动一个字符,即 'y' 和 '1' 中间,进行第三次匹配尝试,匹配到 ''。
然后往前移动一个字符,即 '1' 和 '2' 中间,进行第四次匹配尝试,匹配到 ''。
然后往前移动一个字符,即 '2' 和 '3' 中间,进行第五次匹配尝试,匹配到 ''。
然后往前移动一个字符,即串尾,进行第五次匹配尝试,匹配到 ''。

任何文本都包含 ''。
DiamondbacK
2016-11-06 19:25:44 +08:00
更正

'xy123' 长度为 5 ,所以 re.findall 最多可以在 6 个位置 (想像光标插入位置) 尝试匹配。
第一个位置是在字符串首,匹配到了 'x'。
然后跳过匹配到的字符串,移到 'y' 前面,进行第二次匹配尝试,匹配到 ''。
然后往前移动一个字符,即 'y' 和 '1' 中间,进行第三次匹配尝试,匹配到 ''。
然后往前移动一个字符,即 '1' 和 '2' 中间,进行第四次匹配尝试,匹配到 ''。
然后往前移动一个字符,即 '2' 和 '3' 中间,进行第五次匹配尝试,匹配到 ''。
然后往前移动一个字符,即串尾,进行第六次匹配尝试,匹配到 ''。

任何文本都包含 ''。
jin6220
2016-11-06 19:33:24 +08:00
@noli 你看看 @DiamondbacK 写的 更详细。
jin6220
2016-11-06 19:46:11 +08:00
@DiamondbacK 刚才刚好去搜索了 互联网找到这份内容:
’ 对于字符串” 123 “而言,包括三个字符四个位置。正则表达式匹配过程中,如果子表达式匹配到东西,而并非是一个位置,并最终保存到匹配的结果当中。这样的就称为占有字符,而只匹配一个位置,或者是匹配的内容并不保存到匹配结果中,这种就称作零宽度,后续会讲到的零宽度断言等。占有字符是互斥的,零宽度是非互斥的。也就是一个字符,同一时间只能由一个子表达式匹配,而一个位置,却可以同时由多个零宽度的子表达式匹配。‘

然后看到您的回复,感觉好像是这样子:
源文件是由两个部分组成,可以看到的占有字符,另一部分是字符两边的位置。
xy123 是 5 个字符, 6 个位置。
不过这样的话 6 个位置都符合 x?中 0 次的空集合啊,那这样结果是少匹配到一次 ''???
这种东西深入理解还要从原理上理解。
或者从例子中实践记住结果,当个实践派。
DiamondbacK
2016-11-06 19:50:08 +08:00
@jin6220
/x?/ 是贪婪匹配,匹配尽可能长的字符串,能匹配 'x' 就不匹配 ''。
jin6220
2016-11-06 20:31:34 +08:00
源字符: xyx
或者写成 。 x 。 y 。 x 。(用。表示位置)
表达式: x?
Match 1
Full match 0-1 `x`
Match 2
Full match 1-1 ``
Match 3
Full match 2-3 `x`
Match 4
Full match 3-3 ``
=。=。=。=。=。=
>>> re.findall(r'x?','xyx') #正则匹配模式含有空集合
['x', '', 'x', '']
>>> re.findall(r'x??','xyx')
['', '', '', '']
通过实践可知,如果正则匹配模式含有空集合的话,匹配的时候,字符与位置是同时参与的,之前理解的是根据先后关系先字符前面的位置然后是字符。两者同时参与正则模式匹配,根据贪婪模式或者非贪婪模式选其一(匹配字符或者位置)。

@DiamondbacK
thekoc
2016-11-07 09:35:54 +08:00
你需要看《精通正则表达式》。

如果没有一个系统的学习,这些类似困惑会在每次使用正则表达式的时候都阴魂不散…
samtoto
2016-11-07 11:27:55 +08:00
In [2]: re.findall('x{1}', 'xy123')
Out[2]: ['x']

In [3]: re.findall('x{0,1}', 'xy123')
Out[3]: ['x', '', '', '', '', '']
irenicus
2016-11-07 18:52:11 +08:00
你需要看精通正则表达式+1
贪婪模式下引擎是个吃货,一口吃饱,然后吐出来
第一步,从
[xy123] []
开始,一口口吐出来,直到
[x] [y123]
符合了 x ?

第二步从
[y123] []开始
直到
[][y123]
也符合 x ?,因为有 0 个 x

这次引擎一个字符都没吞,如果不吞一个的话,就死循环了,所以必须吞掉一个字符, y 没了

第三步,从
[123] []开始,后面和第二步一样

直到第五步结束后,强行吞掉 3 ,字符串已经没有了
所以引擎停止,总共执行了 5 次,五次都匹配到了
irenicus
2016-11-07 18:54:09 +08:00
至于你这个为啥是六次,我就不知道了,可能是 3 后面还有个\n
mingyun
2017-07-02 13:57:28 +08:00
@DiamondbacK 学习了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/318505

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX