在python中,如何使用正则表达式有条件地模式匹配

Aid*_*len 3 python regex conditional conditional-regex

我试图用python的正则表达式库解析以下字符串:

recipe_a = 'run_list[sm_collectd::default@1.0.0]'
Run Code Online (Sandbox Code Playgroud)

使用http://pythex.org/,我正在尝试以下正则表达式:

\[(.*)::(.*)@(.*)\]
Run Code Online (Sandbox Code Playgroud)

产量:

Match 1
    1.  sm_collectd
    2.  default
    3.  1.0.0
Run Code Online (Sandbox Code Playgroud)

这是问题所在:

recipe_a可以与此正则表达式进行模式匹配,但是,当字符串中不再指定@version时,它会失败.以下示例将无法匹配模式:

recipe_b = 'run_list[sm_collectd::default]'
Run Code Online (Sandbox Code Playgroud)

\\[(.\*)::(.\*)@(.\*)\\]在这种情况下失败,因为@从未匹配.有python逻辑,\\[(.\*)::(.\*)@(.\*)\\]试图并尝试后一个正则表达式\\[(.\*)::(.\*)\\].但这很愚蠢.如果我能用一个正则表达式模式完成这个,那就太好了.

我试过用条件正则表达式语句解决这个问题.我尝试过的一般语法如下:

(?(?=regex)then|else)
Run Code Online (Sandbox Code Playgroud)

首先 ?是先行断言:没有消费的匹配.所以我们可以对@符号进行条件匹配.

如果@匹配则执行\\[(.\*)::(.\*)@(.\*)\\],否则执行\\[(.\*)::(.\*)\\].

程序化解决方案

kitchen_recipe = 'recipe[my_cookbook::default@0.1.0]'

recipe = kitchen_recipe.strip('recipe[').strip(']')
if '@' in recipe:
    cookbook, recipe, cookbook_version = tuple(re.split('::|@', recipe))
else:
    cookbook, recipe = tuple(re.split('::', recipe))
    cookbook_version = None   # no version specified
Run Code Online (Sandbox Code Playgroud)

REGEX解决方案

kitchen_recipe = 'recipe[my_cookbook::default@0.1.0]'

run_list_pattern = '\[(.*)::([^@]*)@?([0-9.]*)\]'
cookbook, recipe, cookbook_version = re.search(test_list_pattern,
                                               kitchen_recipe).groups()
Run Code Online (Sandbox Code Playgroud)

Ker*_*nic 5

一些小的改动应该适合您的目的; 尝试\[(.*)::([^@]*)(?:@(.*))?\]

run_list[sm_collectd::default] 产量

  1. sm_collectd
  2. default
  3. 没有

run_list[sm_collectd::default@1.0.0] 产量

  1. sm_collectd
  2. default
  3. 1.0.0

说明:

我把决赛@(.*)改成了(?:@(.*))?.附加(?:)是一个非捕获基团,下面?的意思是"匹配0或1倍".

我也改变了第一个(.*)进入([^@]*),这意味着匹配任何东西,但一个@多次越好,这样它不会把所有default@1.0.0的厨师配方名.


编辑:正如@gregory指出的那样,你可以避免非捕获组.你可以用\[(.*)::([^@]*)@?(.*)\].

如果您想将版本号限制为仅数字和.s : \[(.*)::([^@]*)@?([0-9.]*)\].