如何捕获和格式化输出嵌套格式标签?

How can I capture and format on output nested format tags?

我正在开发一个论坛系统,它像 [b]some bold text[/b] 一样解析 BBCode,并在通过 PHP 输出时应用 HTML 格式。我的所有表达式都有效,但我无法弄清楚如何处理特定情况,特别是关于嵌套引号块。

在论坛上,您可能让一个用户引用另一个用户的话。我已经成功地使用以下格式对其进行了格式化:

#\[quote="(.*?);(\w*?)"\]\s*(.*?)\s*\[\/quote\]#

并调用 preg_replace() 将其替换为:

<blockquote id="quote-"><p><br> - </p></blockquote>这里有个working example.

对于您可能在论坛上看到的真实示例,用户 Stan 想要引用 John,将其添加到文本区域以供提交:

[quote="John;2"]John's sentence[/quote] 
____________

Stan's reply

但是如果约翰在他的 post 中引用了玛丽的话会怎样?

[quote="John;2"][quote="Mary;1"]Mary's sentence[/quote]John's sentence[/quote]
____________

Stan's reply

我的正则表达式将捕获除最后一个 [/quote] 之外的所有字符串,但即使我能够捕获整个字符串,我也不确定如何格式化它。理想情况下,我希望输出看起来像这样:

    "Mary's sentence"          
        - Mary

"John's sentence"
    - John
__________________________

Stan's reply

在HTML中:

<blockquote id="quote-2">
    <blockquote id="quote-1"><p>"Mary's sentence"<br> - Mary</p></blockquote>
        <p>"John's sentence"<br> - John</p>
</blockquote> 
<p>Stan's reply</p>

我可以使用正则表达式捕获和格式化重复的嵌套标签吗?如果有 100 个嵌套的引用块怎么办?显然我可以写一个长得离谱且重复的表达式(这肯定会有局限性),但必须有更好的方法来解决这个问题。我应该使用其他方法吗?

很抱歉 similar question already exists,但我已经查看了很多关于 SO 的问题,但仍然不确定我应该采用哪种方法。

我们的想法是确保您只匹配最里面的 BB 标签。匹配 [quote[/quote] 之间不包含另一个 [quote= 的所有文本,并替换直到找不到这样的匹配项。它也基于您的实际标签内容中没有 [quote= 的假设,但在大多数情况下它是正确的。另一个假设是属性是 "-quoted 并且里面不能有其他双引号。

所以,您可以使用

$s = '[quote="John;2"][quote="Mary;1"]Mary\'s sentence[/quote]John\'s sentence[/quote]';
$repl = '<blockquote id="quote-"><p> <br> - </p></blockquote>';
$reg = '~\[quote="([^"]*);(\w*)"]\s*((?:(?!\[quote=).)*?)\s*\[/quote]~si';
while (preg_match($reg, $s)) {
    $s = preg_replace($reg, $repl, $s);
}
echo $s;
// => <blockquote id="quote-2"><p><blockquote id="quote-1"><p>Mary's sentence <br> - Mary</p></blockquote>John's sentence <br> - John</p></blockquote>

PHP demo。正则表达式是

'~\[quote="([^"]*);(\w*)"]\s*((?:(?!\[quote=).)*?)\s*\[/quote]~si'

参见regex demo

详情

  • \[quote=" - 文字子串
  • ([^"]*) - 捕获第 1 组:除 "
  • 之外的任何 0+ 个字符
  • ; - 冒号
  • (\w*) - 捕获第 2 组:0+ 个单词字符
  • "] - 文字子串
  • \s* - 0+ 个空格
  • ((?:(?!\[quote=).)*?) - 捕获第 3 组:任何字符,尽可能少,不开始 [quote= text
  • \s* - 0+ 个空格
  • \[/quote] - 文字 [/quote] 子字符串。

漂亮打印是一项额外的任务,还有a couple of solutions mentioned here