请教一个 Python 正则表达式问题

2018-01-25 15:03:05 +08:00
 feng32
Python 2.7.6 (default, Nov 23 2017, 15:49:48) 
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> re.search("(switchport)?", "  switchport trunk allowed vlan 10").groups()
(None,)
>>> re.search("(switchport)", "  switchport trunk allowed vlan 10").groups()
('switchport',)

如上,为什么加了问号后,匹配出来的就变成 None 了?难道问号不能作用于一个 group ?

如果我要用从 m.groups() 中判断是否匹配了特定关键字,需要怎么写?

2005 次点击
所在节点    程序员
18 条回复
Enochyun
2018-01-25 15:22:59 +08:00
你匹配的内容里面没有?号 当然是 none 了。
feng32
2018-01-25 15:23:58 +08:00
@Enochyun 问号不是代表 0 个或 1 个表达式的含义吗?
wlsnx
2018-01-25 15:24:41 +08:00
In [23]: re.search?
Signature: re.search(pattern, string, flags=0)
Docstring:
Scan through string looking for a match to the pattern, returning
a match object, or None if no match was found.
File: ~/.pyenv/versions/3.6.4/lib/python3.6/re.py
Type: function

找到一个匹配就返回,很明显 "(switchport)?" 可以匹配 ""
feng32
2018-01-25 15:33:25 +08:00
@wlsnx 那么如果我要实现匹配 n 个可选关键字,应该就不能把正则写成 (aaa)?( bbb)?( ccc)? 的形式了,这种情况有解决方法吗?
imn1
2018-01-25 15:33:51 +08:00
没看过源码,但从经验来看,py 的 re 基本上是最小匹配优先
例如 findall 如果出现两个“零或多个”不定长度,匹配结果就往往不是预期结果了
geelaw
2018-01-25 15:34:40 +08:00
你可以写 aaa|bbb|ccc,如果你一次只想要一个。

或者采用展开的形式:aaa(bbb)?(ccc)?|bbb(ccc)?|ccc
wlsnx
2018-01-25 15:36:21 +08:00
你在这些可选关键字周围加一些必选关键字就行了
scriptB0y
2018-01-25 15:39:15 +08:00
这是一个正则表达式的问题,和 Python 没什么关系。

你应该看一下贪心 match 和惰性 match 的却别 https://stackoverflow.com/questions/2301285/what-do-lazy-and-greedy-mean-in-the-context-of-regular-expressions

> 那么如果我要实现匹配 n 个可选关键字

这个在 python 里面不用匹配,用搜索比较好一些。re.findall(r"(aaa|bbb|cc)", string)
Aevery
2018-01-25 15:40:40 +08:00
*相当于匹配最少 0 到无限次, +相当于匹配最少 1 次到无限次,而 ?是匹配最少 0 次到最多 1 次。。。lz 可以试试用”+“
feng32
2018-01-25 15:48:57 +08:00
@scriptB0y 实际上更完整的场景是这样的

我设计了一个简单的模板语言,支持 blablabla {var1} {var2} bla <flag1> <flag2> blabla 这样的语法

{var1} 是一个变量,编译成了正则表达式 ([^ ]+)
<flag1> 是一个可选参数,可以出现 0 次 或 1 次,编译成了正则表达式 (flag1)?

最后将所有捕捉到的 regex groups 再映射到原变量返回给用户

这个模板本身是灵活的,用户可以把 <flag1> 放在最前面,甚至也可以一行就一个 <flag1>

这种情况下感觉启用贪婪匹配模式就可以解决问题了吧?
p2pCoder
2018-01-25 15:48:58 +08:00
```
import re
re.search("(switchport)?", " switchport trunk allowed vlan 10").groups()
(None,)

re.findall("(switchport)?", " switchport trunk allowed vlan 10")
['',
'',
'switchport',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'']

re.search("(switchport)+", " switchport trunk allowed vlan 10").groups()
('switchport',)

re.findall("(switchport)+", " switchport trunk allowed vlan 10")
['switchport']
```
feng32
2018-01-25 15:53:06 +08:00
@p2pCoder 我在楼上补充了一下实际的场景,findall 虽然也能捕捉到目标字符,但是干扰项有点太多了
p2pCoder
2018-01-25 15:57:49 +08:00
@feng32 所以为什么不像 9 楼说的 用+
feng32
2018-01-25 15:59:49 +08:00
@p2pCoder 因为 flag1 可以不存在
scriptB0y
2018-01-25 16:09:14 +08:00
@feng32 你这是一个词法分析了吧,可以看一下内置的库 https://docs.python.org/3.6/library/shlex.html
feng32
2018-01-25 16:20:26 +08:00
@scriptB0y 实际上就是一个以正则表达式为中间代码的模板解析器,当我用 30 行 Python 把它写出来的时候自己也挺惊讶的,而且发现它也挺好用的,今天刚遇到这个问题

正规的词法分析、语法分析之前上大学的时候也做过,不过暂时不准备替换重量级的方案; shlex 这个库听过但是没详细了解,后面可以花点时间看看
windvans
2018-01-25 21:11:24 +08:00
?可以匹配 0 次,这个可能是和 python 的 re 匹配原则有关,如果是贪心的话,应该先匹配有的

你这个确实用 find 比较好
zhangsen1992
2018-01-26 11:55:23 +08:00

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

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

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

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

© 2021 V2EX