如何使用正则表达式进行分组匹配,且分组数量不确定

35

我想在程序输出日志上执行正则表达式匹配(Python)。该日志包含一些看起来像这样的行:

... 
VALUE 100 234 568 9233 119
... 
VALUE 101 124 9223 4329 1559
...
我想捕获在以VALUE开头的行的第一次出现之后出现的数字列表。例如,我希望它返回('100','234','568','9233','119')。问题是,我不知道预先有多少个数字。
我尝试使用这个正则表达式:
VALUE (?:(\d+)\s)+

这个正则表达式匹配了这一行,但它只捕获了最后一个值,所以我只得到('119',)。


1
根据之前的问题,我发现使用正则表达式是不可能的:https://dev59.com/UnRB5IYBdhLWcg3w7riV - Lorin Hochstein
6个回答

28

你需要的是一个解析器,而不是正则表达式匹配。在你的情况下,我会考虑使用一个非常简单的解析器,split()函数:

s = "VALUE 100 234 568 9233 119"
a = s.split()
if a[0] == "VALUE":
    print [int(x) for x in a[1:]]
你可以使用正则表达式来查看输入行是否与所期望的格式匹配(使用你问题中的正则表达式),然后您可以运行上面的代码,而无需检查"VALUE"并且知道int(x)转换始终成功,因为您已经确认以下字符组均为数字。

14
>>> import re
>>> reg = re.compile('\d+')
>>> reg.findall('VALUE 100 234 568 9233 119')
['100', '234', '568', '9223', '119']

这并不能验证关键字“VALUE”是否出现在字符串开头,也不能验证项之间是否恰好有一个空格,但如果您可以将其作为单独步骤完成(或者根本不需要完成),那么它将在任何字符串中查找所有数字序列。


4
这里没有介绍的另一个选项是设置一堆可选捕获组。
VALUE *(\d+)? *(\d+)? *(\d+)? *(\d+)? *(\d+)? *$

这个正则表达式可以捕获由空格分隔的最多5个数字组。如果需要更多的数字组,请复制并粘贴更多的*(\d+)?块。


2
你可以先运行主要的匹配正则表达式,然后在这些匹配项上运行第二个正则表达式以获取数字:
matches = Regex.Match(log)

foreach (Match match in matches)
{
    submatches = Regex2.Match(match)
}

当然,如果您不想编写完整的解析器,这也是可以的。

4
这是哪种编程语言? - mwil.me

1
我遇到了同样的问题,我的解决方法是使用两个正则表达式:第一个匹配我感兴趣的整个组,第二个解析子组。例如,在这种情况下,我会从这里开始:
VALUE((\s\d+)+)

这将产生三个匹配项:[0] 整行,[1] value 后的内容,[2] 最后一个空格+value。
[0] 和 [2] 可以忽略,然后可以使用 [1] 与以下内容一起使用:
\s(\d+)

注意:这些正则表达式未经过测试,但我希望你能理解它们的意思。
格雷格的答案对我来说不起作用的原因是,解析的第二部分更加复杂,不仅仅是一些由空格分隔的数字。
然而,对于这个问题,我会诚实地采用格雷格的解决方案(它可能更有效率)。
我只是写下这个答案,以防有人像我一样需要一个更复杂的解决方案。

0
你可以使用 re.match 进行匹配检查,然后调用 re.split 使用正则表达式作为分隔符进行分割。
>>> s = "VALUE 100 234 568 9233 119"
>>> sep = r"\s+"
>>> reg = re.compile(r"VALUE(%s\d+)+"%(sep)) # OR r"VALUE(\s+\d+)+"
>>> reg_sep = re.compile(sep)
>>> if reg.match(s): # OR re.match(r"VALUE(\s+\d+)+", s)
...     result = reg_sep.split(s)[1:] # OR re.split(r"\s+", s)[1:]
>>> result
['100', '234', '568', '9233', '119']

分隔符"\s+"可以更加复杂。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接