【问题标题】:preg_replace : getting a html tag inside an other html tag from BBCodepreg_replace : 从 BBCode 获取另一个 html 标记内的 html 标记
【发布时间】:2026-02-23 03:25:02
【问题描述】:

所以我正在尝试制作一个 php 函数来从 BBCode 样式 表单中获取 HTML 标记。事实是,我可以很容易地使用 preg_replace 获得标签。但是当我在同一个 bbcode 中有一个 bbcode 时,我会遇到一些麻烦......

像这样:

[blue]My [black]house is [blue]very[/blue] beautiful[/black] today[/blue]

所以,当我“解析”它时,我总是保留蓝色的 bbcode。类似的东西:

My house is [blue]very[/blue] beautiful today

除了第一个蓝色标签内的黑色标签内的蓝色标签外,所有内容都是彩色的。

我怎么能这样做?

有了更多信息,我尝试了:

Regex: "/\[blue\](.*)\[\/blue\]/si" or "/\[blue\](.*)\[\/blue\]/i"
Getting : "My house is [blue]very[/blue] beautiful today"

Regex : "/\[blue\](.*?)\[\/blue\]/si" or "/\[blue\](.*)\[\/blue\]/Ui"
Getting : "My house is [blue]very beautiful today[/blue]"

我必须循环 preg_replace 吗?有没有办法做到这一点,regex-style,不循环的东西?

感谢您的关注。 :)

【问题讨论】:

  • 我建议搜索“php bbcode library”是你想看的地方。将其解析为 HTML,然后使用适当的 DOM 处理工具进行处理。不要试图重新发明*。
  • 您能否进一步澄清一下?据我了解,您正在用 html 标签替换 BBCode 标签?
  • @UmurKaragöz 没错。它从一个 bbcode 开始,我想要它在 html 标签中! miken32 你说得对,我不应该重新发明*,但是我很好奇,我想知道我怎么能做到这一点:)
  • 请不要手动编写简码,使用像我的简码这样完善的库,它可以让你用你想要的任何东西替换它们:github.com/thunderer/Shortcode

标签: php regex preg-replace


【解决方案1】:

您不应该在产品上重新发明*,而是选择经过良好测试的插件是正确的。但是,如果您正在试验或从事宠物项目,请务必继续进行试验,享受乐趣并在此过程中获得重要知识。

话虽如此,您可以尝试遵循正则表达式。我会在下面为你分解。

(\[(.*?)\])(.*?)(\[/\2\])

哲学

在像这样解析标记时,您真正想要的是将标签与它们的对匹配

因此,您可以采取的一种干净的方法是运行一个循环并每次捕获最外层的标记对并替换它。

因此,在上面给定的正则表达式中,捕获组将为您提供以下信息;

  1. 开始标签(完整)[black]
  2. 开始标签(标签名称)black
  3. 开始和结束标记My [black]house is [blue]very[/blue] beautiful[/black] today之间的内容
  4. 结束标签[/blue]

因此,您可以使用 $2 来确定您正在处理的标签,并将其替换为

<tag>$3</tag>
// or even
<$2>$3</$2>

哪个会给你;

// in first iteration
<tag>My [black]house is [blue]very[/blue] beautiful[/black] today</tag>

// in second iteration
<tag>My <tag2>house is [blue]very[/blue] beautiful</tag2> today</tag>

// in third iteration
<tag>My <tag2>house is <tag3>very</tag3> beautiful</tag2> today</tag>

代码

$text = "[blue]My [black]house is [blue]very[/blue] beautiful[/black] today[/blue]";

function convert($input)
{
    $control = $input;

    while (true) {
        $input = preg_replace('~(\[(.*?)\])(.*)(\[/\2\])~s', '<$2>$3</$2>', $input);

        if ($control == $input) {
            break;
        }

        $control = $input;
    }

    return $input;
}


echo convert($text);

【讨论】:

  • 不错的...我去看看。感谢您的帮助:) 但是,实际上,您的 ase 使用循环......循环所有模式和替换不是更容易吗?因为这个循环可能会持续很长时间,或者我错过了什么?
  • 递归处理是解决这个问题的唯一方法。因此,在任何一种情况下都会发生迭代。你只能把它放在幕后。我添加了一些代码示例。如果您测试它,请通知我,我很想知道它是否具有良好的性能。
  • 不错的代码,我刚刚测试了它:它似乎工作得很好。但是,我不想用 html 标签替换所有 BBcode“标签”(比如 [thing] 不应该被 替换)......所以我猜我可以使用我的 $pattern, $替代品?我只是看一下 preg_replace_callback 的东西,但人们似乎说“不要将它用于 BBcode !!”... ^^
  • 当然,您可以将preg_replace 替换为preg_replace_callback 并像这样使用它。我自己之前没有解析过 bbcode,但在你的位置上,我会使用一些流行的 bbcode 转换器并检查它们,看看它们是如何处理任务的。并询问“人们”还有什么选择。
  • 在所有情况下:非常感谢!我会处理你和@Jan 给我看的东西^^
【解决方案2】:

正如其他人提到的,不要试图重新发明*。
但是,您可以使用 递归 方法:

<?php

$text = "[blue]My [black]house is [blue]very[/blue] beautiful[/black] today[/blue]";

$regex = '~(\[ ( (?>[^\[\]]+) | (?R) )* \])~x';

$replacements = array(  "blue" => "<bleu>", 
                        "black" => "<noir>", 
                        "/blue" => "</bleu>",
                        "/black" => "</noir>");

$text = preg_replace_callback($regex,
    function($match) use ($replacements) {
        return $replacements[$match[2]];
    },
    $text);

echo $text;
# <bleu>My <noir>house is <bleu>very</bleu> beautiful</noir> today</bleu>

?>

在这里,每个颜色标签都被替换为法语(只是编造的)对应物,请参阅a demo on ideone.com。要了解有关递归模式的更多信息,请查看有关该主题的 PHP documentation

【讨论】:

  • 非常感谢这些信息。我去看看!