正则表达式捕获重复模式

3
我一直在网上搜寻如何构建正则表达式以捕获我需要的文本; 所以我看到了一些StackOverflow的问题,但他们都没有表达我想要的。如果你看过类似于我的问题的文章,请随时指出来...
我尝试使用递归,但似乎我不够好使它工作。
注意:
1)我不能使用解析程序,因为将使用这些数据的程序将使用正则表达式来捕获它,而这个程序是一个“通用”程序,实际上捕获任何需要的数据,唯一需要做的就是提供适当的正则表达式以获取所需信息,并使其尽可能紧凑,因此我不能使用第三方或外部程序。
2)'key':'value'对可以变化,它们不总是相同数目的配对...这就是我认为它很困难的原因。
3)将使用此正则表达式的程序在Python 2.7.3中创建: 此程序的工作方式: 它使用一个Json配置文件,在那里我可以设置我想要运行的命令,从而给我需要的数据,然后我指定一个正则表达式来教导程序需要捕获什么以及如何处理它,即: 对于被捕获的组要做什么...这就是为什么我不能使用解析器的原因。 此程序使用fabric来运行配置收集器(带有正则表达式)到远程主机并收集所有数据...
4)该程序用于收集数据以将其发布到web服务器,并获取度量和其他东西,如图表和监视报警等。
我已经能够捕获我计划捕获的几乎所有数据,但是当我尝试为此创建一个收集器时,我卡住了...
以下数据与下面完全重复,但具有不同的服务器名称,当然值也会改变:
Server: Omega-X
celery.queue_length: {'transfer_data': '0', 'factor_a': '0', 'slow': '0', 'factor_b': '0', 'score_retry': '0', 'damage_factor_c': '0', 'voice_ud': '0', 'alarm_factors_bl': '0', 'telemetry_x': '0', 'endstream': '0', 'celery': '0', 'awl': '0', 'prs': '0', 'score': '0', 'feature_factors_xf': '0', 'feature_factors_dc': '0'}


Server: Alfa-X
celery.queue_length: {'transfer_data': '0', 'factor_a': '0', 'slow': '0', 'factor_b': '0', 'score_retry': '0', 'damage_factor_c': '0', 'voice_ud': '0', 'alarm_factors_bl': '0', 'telemetry_x': '0', 'endstream': '0', 'celery': '0', 'awl': '0', 'prs': '0', 'score': '0', 'feature_factors_xf': '0', 'feature_factors_dc': '0'}

我想如何捕获它:

Server: Omega-X

 transfer_data: 0
 factor_a: 0
 slow: 0
 factor_b: 0
 score_retry: 0
 damage_factor_c: 0
 voice_ud: 0
 alarm_factors_bl: 0
 telemetry_x: 0
 endstream: 0
 celery: 0
 awl: 0
 trx: 0
 points: 0
 feature_factors_xf: 0
 feature_factors_dc: 0

Server: Alfa-X

 transfer_data: 0
 factor_a: 0
 slow: 0
 factor_b: 0
 score_retry: 0
 damage_factor_c: 0
 voice_ud: 0
 alarm_factors_bl: 0
 telemetry_x: 0
 endstream: 0
 celery: 0
 awl: 0
 trx: 0
 points: 0
 feature_factors_xf: 0
 feature_factors_dc: 0

如果显示的是唯一的服务器,那么使用下面的正则表达式可以捕获所有内容(除了服务器名称):
'([a-z_]+)':\s'(\d+)'

这个正则表达式只会提取第二部分,也就是变量和值的列表,但不包括服务器名... 所以如果在相同的输出中有多台服务器使用了相同的数据,那么就无法知道这些值来自哪个服务器...

如果我尝试添加对服务器名称的支持: 我尝试了下面这个正则表达式,它可以工作,但只能捕获服务器名称和第一组参数:

Server:\s([a-zA-Z0-9-]+)\s*celery\.queue_length:\s.('([a-z_]+)':\s'(\d+)')*

我尝试了多个递归功能,但都未能实现我想要的效果。

有没有人可以指引我正确的方向呢...?

谢谢。


你的第一个模式之所以有效,是因为它逐个匹配了每个键/值对。听起来你现在想要做的是用一个正则表达式模式一次性处理整个集合。让你的程序像这样捕获任意数量的组可能是不可能的(但如果数量相同,就不会那么糟糕,就像你上面提到的那样)。与其专注于捕获键/值数据,不如使用格式化操作,例如匹配 celery\.queue_length: \{|, 并替换为 \n?请参见:https://regex101.com/r/rzmJgj/1 - CAustin
嗨,CAustin,感谢您的回复。这可能很有用,但仍需要一些清理,正如您所提到的那样。睡了一晚上后,我开始像您和其他人一样思考,几乎不可能仅通过正则表达式实现,因为我还需要捕获组... - Larry
我不得不承认我需要违反自己的规则,因为我想要的对于程序使用正则表达式来实现确实非常困难。我的意思是,我的程序需要有组来收集数据,所以在这种情况下,使用正则表达式将会给我n个可能无法正确处理的组...所以可能需要一个预处理程序... - Larry
3个回答

1

您想要键值对?使用Python,我会使用字典。

  1. 获取服务器名称和包含数据的字符串:
    Server: ([^\n]*)(?:[^{]*)\{(.*)\}

  2. 为每个服务器构建包含数据的字典:

使用Python(只需要import re语句):

input = """Server: Omega-X
celery.queue_length: {'transfer_data': '0', 'factor_a': '0', 'slow': '0', 'factor_b': '0', 'score_retry': '0', 'damage_factor_c': '0', 'voice_ud': '0', 'alarm_factors_bl': '0', 'telemetry_x': '0', 'endstream': '0', 'celery': '0', 'awl': '0', 'prs': '0', 'score': '0', 'feature_factors_xf': '0', 'feature_factors_dc': '0'}

Server: Alfa-X
celery.queue_length: {'transfer_data': '0', 'factor_a': '0', 'slow': '0', 'factor_b': '0', 'score_retry': '0', 'damage_factor_c': '0', 'voice_ud': '0', 'alarm_factors_bl': '0', 'telemetry_x': '0', 'endstream': '0', 'celery': '0', 'awl': '0', 'prs': '0', 'score': '0', 'feature_factors_xf': '0', 'feature_factors_dc': '0'}"""


for match in re.findall(r'Server: ([^\n]*)(?:[^{]*)\{(.*)\}', input):
    server = match[0]
    data = match[1]
    datadict = dict((k.strip().replace("'", ""), v.strip().replace("'", "")) for k,v in (item.split(':') for item in data.split(',')))
    datadict['serveur'] = server

然后,您可以将每个数据字典存储(例如在列表中),并根据需要使用它们。您可以将值从字符串转换为整数以便于操作。


抱歉mquantin,在发布我的另一个想法之前没有看到你的答案,我也会检查一下你的建议,谢谢! - Larry
嗨mquantin,你知道吗,我喜欢这个方法,因为它可以在不改变原始格式的情况下捕获所有内容,我认为这样更好...我一直在避免进入收集器的Python代码,只是为了这个特定的输出,但我认为没有其他简单的方法来实现它...非常感谢,我想我会采用这种方法。 - Larry

0

你可以使用Antlr来定义你的语法,这比正则表达式更好:https://dzone.com/articles/antlr-4-with-python-2-detailed-example

如果你想使用正则表达式,你可以使用以下代码。请注意,我的代码是用C#编写的,但正则表达式在Python中应该表现相同。

string serverNamePattern = @"(?<=Server(\s)*:(\s))\s*[\w-]+";
string dataPattern = @"(?<=celery.queue_length[\s:]*{)[a-zA-Z0-9\s:\'_,]+";
string input = 
    "Server: Omega-X" + 
    "celery.queue_length: {'transfer_data': '0', 'factor_a': '0', 'slow': '0', 'factor_b': '0', 'score_retry': '0', 'damage_factor_c': '0', 'voice_ud': '0', 'alarm_factors_bl': '0', 'telemetry_x': '0', 'endstream': '0', 'celery': '0', 'awl': '0', 'prs': '0', 'score': '0', 'feature_factors_xf': '0', 'feature_factors_dc': '0'}" + 
    "Server: Alfa-X" + 
    "celery.queue_length: {'transfer_data': '0', 'factor_a': '0', 'slow': '0', 'factor_b': '0', 'score_retry': '0', 'damage_factor_c': '0', 'voice_ud': '0', 'alarm_factors_bl': '0', 'telemetry_x': '0', 'endstream': '0', 'celery': '0', 'awl': '0', 'prs': '0', 'score': '0', 'feature_factors_xf': '0', 'feature_factors_dc': '0'}";

var serverNames = Regex.Matches(input, serverNamePattern);
var dataMatches = Regex.Matches(input, dataPattern);

解释:

+:一个或多个出现

\w:字母数字字符

\s:空格字符

[]:定义一个范围

(?<=a)b:正向先行断言,匹配在a后面的b

(?<=Server(\s):(\s))\s[\w-]+:匹配在Server:之后的字母数字字符、破折号和空格字符

(?<=celery.queue_length[\s:]*{)[a-zA-Z0-9\s:\',]+:匹配在celery.queue_length:之后的[a-zA-Z0-9':,\s]范围内的字符。请注意,您需要在服务器名称之前添加“Server:”,并且此操作不会从数据中删除单引号。


嗨,Hooman,我理解你的方法,但不确定是否可以应用于我所拥有的程序。也许可以改变其他正在工作的指标的行为,但无论如何,我可以尝试一下...可能我需要一个新的视角...谢谢! - Larry
抱歉,我之前没有放正确的代码,已经更新了。请注意,匹配返回一个可以迭代的匹配列表,因此正则表达式和匹配组没有问题...但我认为Antlr是处理复杂语法的更好选择。 - Hooman Bahreini
嗨,Hooman。转念一想,一个预解析程序(是的,我知道我这样做是自相矛盾的)将是必需的,以便按照我所希望的方式准备输出... - Larry

0

谢谢你们友善地回答我的问题,我认为你们两个都帮助我重新审视了这个问题...

我相信,我想在这里实现的目标对于正则表达式来说非常困难:

考虑到获取我想要的信息的困难程度,我在思考哪种方式对我来说更容易获取这些信息。所以我知道我违反了自己的规则,但我认为没有其他更顺畅的方法。

如果我想要获取正则表达式组,例如:

Server: Group 0
Key : Group 1
Value: Group 2

那么我的输出应该像这样:

Regex Groups:
        (0)      (1)          (2)         
Server: Omega-X transfer_data: 0
Server: Omega-X factor_a: 0
Server: Omega-X slow: 0
Server: Omega-X factor_b: 0
Server: Omega-X score_retry: 0
Server: Omega-X damage_factor_c: 0
Server: Omega-X voice_ud: 0
Server: Omega-X alarm_factors_bl: 0
Server: Omega-X telemetry_x: 0
Server: Omega-X endstream: 0
Server: Omega-X celery: 0
Server: Omega-X awl: 0
Server: Omega-X trx: 0
Server: Omega-X points: 0
Server: Omega-X feature_factors_xf: 0
Server: Omega-X feature_factors_dc: 0

通过这种方式,我可以在同一输出中处理任意数量的服务器,而不会遇到任何困难,并使用非常简单的正则表达式...
"Server:\s([a-zA-Z_.-]+)\s'([a-zA-Z_]+)':\s'(\d+)'"

所以我认为最好的方法是添加一个Pre-Parser来准备这样的数据,然后处理它...

事实上,你们俩在这方面都帮了我很多,非常感谢。

我想我会关闭这个问题,除非有人有更好的想法 :)


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