PHP中的可选组preg_match_all()正则表达式始终被忽略

时间:2011-08-10 21:19:02

标签: php regex parsing preg-match-all

我一直在试图让一个可选组在preg_match_all()正则表达式中工作,整天撞在砖墙上。非可选版本完美地解析数据,但只要我将正则表达式的一部分作为可选项,该可选部分就永远不会用于解析数据,即使它所针对的行存在于数据中也是如此。

这是有效的原始正则表达式:

$regex = "~:begin(.*)[\r\n]+:desc(.*)[\r\n]+(.*)[\r\n]+:end(?:.*)[\r\n]+~msU";
preg_match_all($regex, $text, $matches);

这是要解析的文本:

  :begin test
  :desc testing
  some code
  more code
  last code
  :end test

  :begin test2
  :desc testing2
  some code2
  last code2
  :end test2

正则表达式将以“:desc”开头的行正确解析为自己的组,但是当我将“:desc”行设置为可选时,同一组始终为空,并且该行将被添加到以下组中, “代码”块的开头。

这是带有desc的可选组的调整后的正则表达式:

$regex = "~:begin(.*)[\r\n]+(:desc(.*)[\r\n]+)?(.*)[\r\n]+:end(?:.*)[\r\n]+~msU";

我相信我明白发生了什么 - 只是不知道为什么或如何解决问题。显然,因为在代码块的开头没有某种明确的标记,当前一行是可选的时,正则表达式绕过可选组并将其全部放入随后的代码块中。我已经尝试过使用标志,将组更改为贪婪/非贪婪的各种组合,但是没有插入类似“:code”前缀的东西来指示下一个块的开始,我就是无法停止将可选行放在代码块之后的正则表达式。

我只想让单行:desc语句可选,而不必为数据添加更多标签或分隔符。

此时,我陷入困境,需要一些资深的正则表达专家来解释发生了什么,以及如何解决(如果可能的话)。

1 个答案:

答案 0 :(得分:1)

否定前瞻可以帮到这里:

~:begin (.*)[\r\n]+(?::desc (.*)[\r\n]+)?^(?!:desc)(?:(.*)[\r\n]+)?:end(?:.*)[\r\n]+~msU

添加的主要部分:^(?!:desc) - 这将检查下一行是否不以开头:desc

我还为可选组添加了(?:...),因此不会为结果数组捕获它们。如有必要,请删除它们。

负面前瞻究竟做了什么?多线和(。)*的主要问题是点匹配(差不多!)任何字符。几乎意味着,换行符Details)除外。但正如你的正则表达式使用“多线模式”,这使得这更加棘手。

让我们将你的第二个正则表达分解为更小的部分:

:begin(.*)[\r\n]+这部分只是找到第一行。我只在这里添加了一个空格以将其从结果中排除。

(:desc(.*)[\r\n]+)?这是您原来的可选方,应该找到第二行。这里也增加了空间。

(.*)[\r\n]+这是代码方,但在您的情况下,这是贪婪的,所以它还找到了:desc 的可选方为了更改这一点,排除了否定前瞻这一部分,并且您希望将其更改为可选,这已更改为:^(?!:desc)(.*)[\r\n]+ - “^”也确保它是新行的开头。

:end(?:.*)[\r\n]+此处无需更改。

其他改进

不确定是否需要或想要,但为了清理语句,我稍微改了一下,这个也捕获了第二个文本块。

~:begin ([^$]*)(?::desc([^$]*))?^(?!:desc)(?:([^$]*))?:end+~msU

此代码使用“$”来检查每行的结尾,因此您不必再检查换行符。