处理递归的正则表达式模式构造

时间:2014-05-12 18:54:25

标签: php regex

我不确定递归是否是描述此模式中出现的内容的正确方法,但不幸的是我对正则表达式来说太新了以构建符合此模式的方式改变并避免嵌套组。

因此模式基本上定义为:

@param {item} {label}:{text} {labeln}:{textn}

其中labelntextn是标签的一个N实例:文本组。

所以一个例子是

/**
 *
 * @param name1 test1:this is text for test1 test2:this is text for test2
 * @param name2 test3:this is text for test3 test4:this is text for test4 test5:this is text for test5
 *
 * /

理想情况下,我尝试将name1test1:this is text for test1test2:this is text for test2作为匹配组进行捕获。 name2行也是如此。当然,可以有更多name1和psuedo"命名参数的例子"可以变化,从无到有。 +编辑:标签文本中不允许使用冒号,因为它们被保留为分隔符。标签是严格的字母数字,标签可能仅限于a-zA-Z0-9 _,'" -

第一个问题是......这是一个递归问题还是我错误地描述了这个问题?

第二个问题是......是否可能,如果可能,我该如何实现?

2 个答案:

答案 0 :(得分:4)

<强>前言

为了便于解释,我决定通过在%前加上“标签”来澄清。这可以是任何保留符号或其他有助于清除标签/文本的模式:

/**
 * @param variable_a %label:This is variable: a %required:true
 * @param variable_b %required:false %pattern:/[a-zA-Z:]/
 */

<强>问题:

在正则表达式中捕获重复模式的问题是你不能拥有an unknown amount of capture groups(即你需要匹配全局匹配数或在每次匹配中捕获特定数量的组):

@param    (?# find a param)
\s*       (?# whitespace)
(\w+)     (?# capture the variable)
\s*       (?# whitespace)
(?:       (?# start non capturing group)
%(\w+):   (?# capture the label)
([^%\n]+) (?# capture the text)
)+        (?# repeat the non-capturing group)

在此示例中,我将标签/文本捕获代码放在非捕获和重复(1次以上)组中。这允许我们匹配整个字符串,但只捕获最后一组标签/文本(因为我们只有3组:变量,标签和文本)。


直截了当的解决方案:

而不是这个,你可以事后match the whole string然后parse the label/text string

(?# match the whole string)
@param    (?# find a param)
\s*       (?# whitespace)
(\w+)     (?# capture the variable)
\s*       (?# whitespace)
(.*)      (?# capture the labels/texts)

(?# parse the label/text string)
%         (?# the start of a label)
(\w+)     (?# capture label)
:         (?# end of label)
([^%]+)   (?# capture text)

非常棒的解决方案:

最后,我们可以使用一些正则表达式魔术来对所有标签/文本组合进行全局匹配。这意味着我们将有一组定义的3个捕获组(变量,标签,文本),我们将有可变数量的匹配。我认为这个最好显示,然后解释,所以这里是crazy awesome regex magic

(?:       (?# start non-capturing group)
  @param  (?# find a param)
  \s*     (?# whitespace)
  (\w+)   (?# capture the variable)
  \s*     (?# whitespace)
 |        (?# OR)
  \G      (?# start back over from our last match)
)         (?# end non-capturing group)
%(\w+):   (?# capture the label)
([^%\n]+) (?# capture the text)

这个围绕着\G的PCRE魔法,它与最后一场比赛的结束相匹配。因此,我们启动一个非捕获组,其中包含@param定义的“前缀”。这将匹配并捕获变量OR从我们上一次匹配结束开始。然后我们匹配/捕获1个标签/文本组。下次重复时,我们将从我们离开的地方开始, 变量捕获组将为空白 (因为它不存在于字符串中,你'我必须使用逻辑来知道你在哪个变量上,并捕获另一个标签/文本组(直到我们点击一​​个新行,因为我说文本不能是%\n) 。然后,下一次匹配尝试将找到由@param定义的新变量。我认为这将是您最好的选择,它只需要更多的逻辑。

答案 1 :(得分:0)

好吧,如果您允许中间标签包含:但您不允许在 end 标签中使用,我相信以下RegEx应该运作良好:

@param\s+(.+?)\s+(.+:.+)\s+([^:]+:[^:]+)$

但是,如果您的模式跨越多行,它将无效。

此外,如果您正在尝试解析 PHPDoc或其中的某些变体,您应该使用RegEx编写自己的解析器。