我正在使用正则表达式解析一些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'
然而,如果我有一个看起来像这样的测试字符串:
<"a">Content<a>Also Content</a></a>
它只匹配
<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"
还有一些其他的错误。
(?: [^<] ++ | (?R)) *
的等价物是(?> [^<]+|(?R)) *
(因为PCRE中的递归本质上是原子操作) ,并且正则表达式引擎在每个组位置(逐组)都可以回溯,当(?: [^<]+|(?R)) *+
的等价物是(?>(?: [^<]+|(?R))*)
时,正则表达式引擎就无法回溯。 - Casimir et Hippolyte