if-else在递归正则表达式中没有按预期工作

Jac*_*ckW 6 php regex pcre

我正在使用正则表达式来解析一些BBCode,因此正则表达式必须递归地工作以匹配其他人中的标签.大多数BBCode都有一个参数,有时引用它,但并非总是如此.

我正在使用的正则表达式的简化等效(使用html样式标记来减少所需的转义)是这样的:

'~<(\")?a(?(1)\1)> #Match the tag, and require a closing quote if an opening one provided
  ([^<]+ | (?R))* #Match the contents of the tag, including recursively
</a>~x'
Run Code Online (Sandbox Code Playgroud)

但是,如果我有一个如下所示的测试字符串:

<"a">Content<a>Also Content</a></a>
Run Code Online (Sandbox Code Playgroud)

它只匹配<a>Also Content</a>因为当它尝试从第一个标记匹配时,第一个匹配组,\1被设置为",并且当递归运行正则表达式以匹配内部标记时,这不会被覆盖,这意味着它不是引用,它不匹配,正则表达式失败.

如果相反我一直使用或不使用引号,它工作正常,但我不能确定那将是我必须解析的内容的情况.有什么方法可以解决这个问题吗?


完整的正则表达式,我使用,来匹配[spoiler]content[/spoiler],[spoiler=option]content[/spoiler]并且[spoiler="option"]content[/spoiler],是

"~\[spoiler\s*+ #Match the opening tag
            (?:=\s*+(\"|\')?((?(1)(?!\\1).|[^\]]){0,100})(?(1)\\1))?+\s*\] #If an option exists, match that
          (?:\ *(?:\n|<br />))?+ #Get rid of an extra new line before the start of the content if necessary
          ((?:[^\[\n]++ #Capture all characters until the closing tag
            |\n(?!\[spoiler]) Capture new line separately so backtracking doesn't run away due to above
            |\[(?!/?spoiler(?:\s*=[^\]*])?) #Also match all tags that aren't spoilers
            |(?R))*+) #Allow the pattern to recurse - we also want to match spoilers inside spoilers,
                     # without messing up nesting
          \n? #Get rid of an extra new line before the closing tag if necessary
          \[/spoiler] #match the closing tag
         ~xi"
Run Code Online (Sandbox Code Playgroud)

不过还有其他一些错误.

Luc*_*ski 3

最简单的解决方案是使用替代方案:

<(?:a|"a")>
  ([^<]++ | (?R))*
</a>
Run Code Online (Sandbox Code Playgroud)

但如果您确实不想重复该a部分,您可以执行以下操作:

<("?)a\1>
  ([^<]++ | (?R))*
</a>
Run Code Online (Sandbox Code Playgroud)

演示

我刚刚将条件放入?组内。这次,捕获组始终匹配,但匹配可以为空,并且不再需要条件。

旁注:我应用了所有格量词来[^<]避免灾难性的回溯


就您而言,我认为匹配通用标签比匹配特定标签更好。匹配所有标签,然后在代码中决定如何处理匹配。

这是完整的正则表达式:

\[
  (?<tag>\w+) \s*
  (?:=\s*
    (?:
      (?<quote>["']) (?<arg>.{0,100}?) \k<quote>
      | (?<arg>[^\]]+)
    )
  )?
\]

(?<content>
  (?:[^[]++ | (?R) )*+
)

\[/\k<tag>\]
Run Code Online (Sandbox Code Playgroud)

演示

请注意,我添加了J选项 ( PCRE_DUPNAMES) 以便能够使用(?<arg>...)两次。