正则表达式和BBCode - 完美嵌套引用

3

我正在为我的网站编写一些BBcode。

我已经成功地使大多数代码完美运行,但是[QUOTE]标签让我感到有些棘手。

当我得到这样的东西时:

[QUOTE=1]
[QUOTE=2]
This is a quote from someone else
[/QUOTE]
This is someone else quoting someone else
[/QUOTE]

它将会返回:
> 1 said:  [QUOTE=2]This is a quote from
> someone else

This is someone else quoting someone else[/QUOTE]

发生的情况是,嵌套引用中的[/quote]关闭了引用块。

我使用的正则表达式是:

"[quote=(.*?)\](.*?)\[/quote\]'is"

如何使嵌套引用正确显示?
谢谢。
3个回答

3
您可以构建递归正则表达式(根据其变更日志,自libpcre-3.0版本起可用):
\[quote=(.*?)\](((?R)|.)*?)\[\/quote\]

但是如果您遵循@codeka的建议会更好。

更新: 这里的(?R)表示“在(?R)出现的地方插入整个正则表达式”。因此,a(?R)?b等价于a(a(?-1)?b)?b,这相当于a(a(a(?-1)?b)?b)?b等等。您可以使用(?N)(?+N)(?-N)(?&a)代替(?R),分别表示“用第N个捕获组替换”、“用下一个第N个捕获组替换”、“用前一个第N个捕获组替换”和“用名为‘a’的捕获组替换”。


好的,这个可以运行,但它没有将引号内的正则表达式转换为HTML标签。 - Moe
要更改所有引号,您必须在匹配时重新应用此正则表达式,可能在每次迭代中递增某些深度变量。 - ZyX
@zylstra,三年多过去了,我无法回忆起这样的细节。猜测某些东西(评论?)在我写完这篇文章后被删除了。(很可能是评论,因为我在这里没有看到已删除的答案。)90%的可能性是,它与Dean Harding在下面写的建议相同:构建一个解析器来代替使用正则表达式。 - ZyX

0

这不是正则表达式擅长的任务。这几乎就像是用正则表达式解析 HTML,我们知道这样会发生什么...

你可以使用 preg_split将输入文本拆分为标签和非标签。即可得到以下列表:

  • [QUOTE=1]
  • (空白)
  • [QUOTE=1]
  • 这是其他人的引用
  • [/QUOTE]
  • 这是其他人引用其他人
  • [/QUOTE]

然后你遍历列表,将标签转换为HTML并输出未修改的纯文本。你甚至可以变得花哨一些,保持“嵌套”计数,这样如果你在不期望的情况下遇到“[/quote]”,你可以比仅输出无效的HTML更好地处理这种情况。或者,你只需按照发现的方式输出内容,让HTMLPurify或其他工具稍后清理它。


嗯,我会尝试一下。看起来似乎没有简单的解决方案! - Moe

0

我花了很长时间来处理这个问题,并希望通过补充尊敬的ZyX的答案来完全关闭这个问题。 他提供了一个非常好的搜索模板,但正如Moe所说,嵌套引用本身并不是这样处理的。
为了解决这个问题,我使用了一个while循环和ZyX的模式,以及preg_replace_callback函数:


$text = 
'
[QUOTE=ctmcn]
    [quote="John Doe;1000"]Lorem ipsum dolor sit amet[/quote]
    Lorem ipsum dolor sit amet
[/QUOTE]
-----------------------------
[QUOTE="John Doe;103318"]
 [QUOTE]
  [QUOTE="John-Doe;103318"]
   Lorem ipsum dolor sit amet
  [/QUOTE]
 Lorem ipsum dolor sit amet
 [/QUOTE]
Lorem ipsum dolor sit amet

 [QUOTE="John-Doe;103318"]
 Lorem ipsum dolor sit amet
 [/QUOTE]
Lorem ipsum dolor sit amet
 [QUOTE="John-Doe;103318"]
 Lorem ipsum dolor sit amet
 [/QUOTE]
Lorem ipsum dolor sit amet
[/QUOTE]
Lorem ipsum dolor sit amet
----------------------------
[QUOTE="John-Doe;103318"]Lorem ipsum dolor sit amet[/QUOTE]
----------------------------
[QUOTE="Максим;103318"]Lorem ipsum dolor sit amet[/QUOTE]
Lorem ipsum dolor sit amet
'

$text = nestedQuotes($text);

function nestedQuotes($text) {
    while (preg_match('#\[quote=?(.*?)\](((?R)|.)*?)\[\/quote\]#is', $text)) {
        $text = preg_replace_callback (
            '#\[quote=?(.*?)\](((?R)|.)*?)\[\/quote\]#is',
            function($m) {
                if ($m[1]) {
                    if (strpos(';', $m[1])) {
                        list($qname, $qpostid) = str_replace('"', '', explode(";", $m[1]));
                        if ($qname && !$qpostid) return 'here code with quotet username and without source post_id, LIKE: [qoute="username";]text[/quote]';
                        if ($qname && $qpostid) return 'here code with quotet username and with source post_id, LIKE: [qoute="username;postid"]text[/quote]';
                    } else {
                        $qname = str_replace('"', '', $m[1]);
                        return 'here code with quotet username and without source post_id [qoute=username]text[/quote]';
                    }
                } else {
                    return 'here anonymous quote, LIKE: [quote]text/quote]';
                }
            },
            $text
        );
        echo $text;
    }
}

像这样,您可以打败任何级别的嵌套和任何类型的引号。
preg_replace_callback函数允许使用函数作为替换参数,借助它,我们可以根据原始引用的形式找出要返回哪个响应(是否可能获取被引用用户的名称、被引用消息的ID等)。 变量$m是带有捕获组的数组。

$m[0] its all captured pattern.
$m[1] its first captured group
$m[2] its second captured group...

等等等等...

但是这种方法有一个缺点。搜索模板非常“贪婪”,在大量文本上我遇到了php工作中的问题。它不返回任何值,也不报告任何错误。 如果有人添加更优化的搜索模板,那将是完美的解决方案。 希望这能帮助到某些人!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接