Python 正则表达式在模式中获取零次或多次出现

3 投票
3 回答
14850 浏览
提问于 2025-04-17 19:51

我有一个能正常工作的正则表达式,它能给我想要的结果,但缺少必要的安全性(防止出错)。

假设我有一个正则表达式,它可以匹配路径的一部分,大致是这样的:

import re
path = "C:/Projects/foo/dev/model/props/furniture/couch/data/fbx"
regex = re.compile("(.+)/dev/model/(.+)/(.+)/data/fbx")
m = regex.search(path)
if m:
    print m.groups()

# ('C:/Projects/foo', 'props/furniture', 'couch')

我想把匹配到的任何字符,直到正则表达式的下一部分替换成可以匹配一个或多个文件夹的东西。

为了简单起见,我们把文件夹定义为以字母数字字符(可以有零个或多个)结尾并带有斜杠的形式,这样就可以表示为:

[\w]*/

我想把这些文件夹分组,数量从零到十个,我该怎么做呢?

在我脑海中,我想到了这样的东西(注意,这个不管用!):

# match any number of word characters ending with a slash zero to ten times
([[\w]*/]{0,10})

# match any number of word characters ending with a slash zero to one time
([[\w]*/]?)

编辑:

根据RedBaron和jamylak的回答,我想出了以下内容:

((?:[:\w]+/){0,3})

这个表达式会把零到三个以斜杠'/'结尾的[:\w]字符分组。因为在分组的开头加了?:,所以这个分组的结果不会被返回。外层的分组会把它们组合在一起,因此我们只会得到完整的分组结果。

唯一的问题是,我希望最后一部分也能匹配一个文件(所以不以斜杠结尾)。我甚至更希望从正则表达式中返回的结果没有尾部的斜杠,但我也可以很容易地把它从结果的末尾去掉。

任何反馈都非常感谢。如果这是正确的方向,我会把它作为答案添加上去。

编辑:

这与:根据预定义的文件夹结构查找文件夹有关。

更新/编辑:

根据到目前为止给出的所有答案,我尝试了多种方法,但最终都非常慢。

import re
path = "C:/Projects/foo/dev/model/props/furniture/couch/data/fbx"
regex = re.compile(r"""((?:^(?:[\w:]+/?)+)|(?:(?<=/)(?:[\w]+/?)+))/dev/model/""")
print 'search start'
m = regex.search(path)
print 'search done'
if m:
    print 'match', m, m.groups()
else:
    print 'no match'

我不太确定怎么才能加快速度!

3 个回答

0

你不能像这样在方括号 [] 里面再放方括号 [],比如这里的 ([[\w]*/]{0,10})。相反,你应该用括号来进行分组。

试试这个:

>>> re.match(r'(\w*/){0,10}', 'abc/def/ghi/').group()
'abc/def/ghi/'
1

这样做会很慢,因为会出现灾难性的回溯

((?:^(?:[\w:]+/?)+)|(?:(?<=/)(?:[\w]+/?)+))/dev/model/

不如试试这个

(^[\w:]+(?:/\w+)*|(?<=/)\w+(?:/\w+)*)/dev/model/

或者这个

(^[\w:]+[\w/]*|(?<=/)[\w/]+)/dev/model/

你可以考虑先用 re.match 来验证字符串,使用像 (\w:)?(/\w+)+$ 这样的模式。

然后,当你已经能预期到某种结构时,可以用更简单的正则表达式来提取数据:

/?([^/]*(?:/[^/]+)*)/dev/model/
4

你在正则表达式中的 [] 并不是用来分组的,而是用来指定字符类别的。

也许这样可以工作-

\w*/{0,10}

如果要分组,就用 ()

(\w*/){0,10}

编辑

根据你编辑后的问题,我觉得你想要的是匹配0到3次目录名,然后再匹配一个文件名。

假设文件名中只包含字母(并且可以有一个最多三位的可选扩展名)。

^((?:[:\w]+/){0,3})(\w+(?:\.\w{1,3})?)?$

这个正则表达式很复杂,但可以分成两部分。

这是你已经有的部分。

((?:[:\w]+/){0,3})

这是我添加的部分。

(\w+(?:\.\w{1,3})?)?

这是最后的一个可选文件名。(如果它不是可选的,你可以去掉最后的 ?)。文件名本身可以只由字母组成,也可以有一个最多三位的扩展名。

添加 ^$ 可以防止错误匹配。

>>> pat=re.compile('^((?:[:\w]+/){0,3})(\w+(?:\.\w{1,3})?)?$')
>>> my_str='fwefw/wfwf/wefwf/dde.cdf'
>>> pat.search(my_str).groups()
('fwefw/wfwf/wefwf/', 'dde.cdf')
>>> my_str='fwefw/dde.cdf'
>>> pat.search(my_str).groups()
('fwefw/', 'dde.cdf')

撰写回答