python re 匹配组

0 投票
2 回答
1766 浏览
提问于 2025-04-17 17:28

我想从字符串中提取一些字段,但我不确定具体有多少个。我用了正则表达式,但遇到了一些我不理解的问题。

比如说:

  199  -> (199)
  199,200  -> (199,200)
  300,20,500 -> (300,20, 500)

我试过了,但总是无法让它正常工作。希望有人能给我一些建议,我会非常感激。

我尝试的正则表达式是:

>>> re.match('^(\d+,)*(\d+)$', '20,59,199,300').groups()
('199,', '300')
// in this, I do not really care about ',' since I could use .strip(',') to trim that. 

我在网上查了一下,试着用 re.findall,但我不确定该怎么做才能得到这个:

>>> re.findall('^(\d+,)*(\d+)$', '20,59,199,300')
[('199,', '300')]

------------------------------------------------------更新

我意识到如果不讲清楚整个故事,这个问题可能会让人困惑。基本上,我想验证在 crontab(或类似的)中定义的语法。

我创建了一个数组 _VALID_EXPRESSION:它是一个嵌套的元组。

 (field_1,
  field_2,
 )

对于每个 field_1,它有两个元组,

 field_1:   ((0,59),        (r'....', r'....'))
            valid_value   valid_format 

在我的代码中,它看起来是这样的:

_VALID_EXPRESSION =  \
 12     (((0, 59), (r'^\*$', r'^\*/(\d+)$', r'^(\d+)-(\d+)$',
 13                 r'^(\d+)-(\d+)/(\d+)$', r'^(\d+,)*(\d+)$')),   # second
 14      ((0, 59), (r'^\*$', r'^\*\/(\d+)$', r'^(\d+)-(\d+)$',
 15                 r'^(\d+)-(\d+)/(\d+)$', r'^(\d+,)*(\d+)$')),   # minute
 16        .... )

在我的解析函数中,我只需要提取所有的组,看看它们是否在有效值的范围内。

我需要的正则表达式之一是能够正确匹配这个字符串 '50,200,300' 并提取出所有的数字。(当然,我可以使用 split(),但那样会背离我最初的意图,所以我不喜欢这个主意。)

希望这对你有帮助。

2 个回答

3

为什么不直接用字符串的split方法呢?

numbers = targetstr.split(',')
1

最简单的解决方案是用正则表达式来实现:

r"(\d+,?)"

你可以用 findall 来获取你想要的 300,20,500。或者,如果你不想要逗号的话:

r"(\d+),?"

这个表达式可以匹配一个或多个数字,后面跟着0个或1个逗号(逗号不在数字组里)。

无论哪种方式:

>>> s = '300,20,500'
>>> r = re.compile(r"(\d+),?")
>>> r.findall(s)
['300', '20', '500']

不过,正如Sahil Grover所指出的,如果这些是你的输入字符串,其实只需要调用 s.split(',') 就可以了。如果你的输入字符串可能包含非数字的内容,这样做可以确保你只匹配数字字符串,但即使这样,使用 filter(str.isdigit, s.split(',')) 可能会更简单。

如果你想要的是一个 tuple(元组)里的 int(整数),而不是一个 list(列表)里的 str(字符串):

>>> tuple(map(int, r.findall(s)))
(300, 20, 500)

如果你觉得列表推导式或生成器表达式比 mapfilter 更容易理解:

>>> tuple(int(x) for x in r.findall(s))
(300, 20, 500)

或者,更简单的方法是:

>>> tuple(int(x) for x in s.split(',') if x.isdigit())
(300, 20, 500)

如果你想要字符串 (300, 20, 500),当然可以通过对 tuple 调用 repr 来实现,但其实有更简单的方法可以得到这个:

>>> '(' + s + ')'
'(300, 20, 500)'

你最初的正则表达式:

'^(\d+,)*(\d+)$'

… 将会返回正好两个组,因为你的模式里正好有两个组。而且,由于你明确地用 ^$ 包裹了它,它必须匹配整个字符串,所以 findall 在这里并没有帮助——它会找到和 match 一样的那个匹配(两个组)。

撰写回答