【问题标题】:How to use preg_replace_callback if i want to use same "string" twice on a callback?如果我想在回调中使用相同的“字符串”两次,如何使用 preg_replace_callback?
【发布时间】:2015-05-10 10:04:02
【问题描述】:

我编写了一个 BBcode 函数来查找匹配项并进行替换。但是,如果我需要使用相同的 preg_match 它不会正确返回匹配。

代码是:

<?php

//CODE EXAMPLE

class BBCode {

protected $bbcode = array();

	public function __construct() {
		// Replace [div class="class name(s)"]...[/div] with <div class="...">...</div>
		$this->bbcode["/\[div class=\"([^\"]+)\"\](.*?)\[\/div\]/is"] = function ($match) {
			return "<div class=\"$match[1]\">$match[2]</div>";
		};	
	}

	public function rander($str) {
		foreach ($this->bbcode as $key => $val) {
			$str = preg_replace_callback($key, $val, $str);
		}
		return $str;
	}
}

?>

如果我只使用一个标签就可以了! 像这样:

$str= "[div class="class1"]this is a div[/div]";

即使我使用不同的标签,它也很有效。

$str= "[div class="class1"][p]this is a  paragraph inside a div[/p][/div]";

但是当我尝试使用时:

$str = "[div class="class1"][div class="class2"]A div inside a div[/div][/div]";

它不工作,输出是:

<div class="class1">[div class="class2"]div inside a div</div>[/div]

代替:

<div class="class1"><div class="class2">div inside a div</div></div>

如何修复它以使其正常工作?

谢谢!

A link to the whole bbcode class code on github

【问题讨论】:

  • 我需要它来使用递归替换
  • 尝试查看 preg_match 而不是 preg_replace
  • 我已经做到了......它似乎找到了第一个与打开标记“[div]”匹配的匹配项,然后用第一个结束标记“[/div]”关闭它,即使那个结束标签不属于该标签。它没有任何区别......不确定它是否可以修复......
  • 它对任何 BBcode 标签都一样...不仅 [div](这只是一个示例...)

标签: php regex preg-replace-callback


【解决方案1】:

您的正则表达式将从第一个 [div class="..."] 匹配到它找到的第一个 [/div]。因此,开始和结束 div 不匹配(如您在示例中所见)。为防止这种情况,当有另一个 [div[/div 时,请使用环视来防止匹配:

"/\[div class=\"([^\"]+)\"\]((?!\[div|\[\/div).)*\[\/div\]/is"

请注意,这只会匹配一次(仅匹配内部的div-pair),因此您必须重复匹配,直到找不到任何内容为止。

演示:

$str = "a[div class=\"b\"]c[div class=\"d\"]e[/div]f[/div]g";
$reg = "/\[div class=\"([^\"]+)\"\]((?!\[div|\[\/div).)*\[\/div\]/is";
preg_match($reg, $str, $matches);
var_dump($matches);

将输出:

array(3)
{
    [0]=>
    string(22) "[div class="d"]e[/div]"
    [1]=>
    string(1) "d"
    [2]=>
    string(1) "e"
}

编辑:是的,正如您评论的那样,它仅替换第一个匹配项。 * 放错地方了。试试这个代码:

$str = 'a[div class="b"]c[div class="d"]e[/div]f[/div]g';
$reg = "/^(.*)(\[div class=\"([^\"]+)\"\])((?!\[div|\[\/div).*)\[\/div\](.*)$/is";

$result = $str;
$step = 1;
echo "step 0: $result\n";
do {
    $result = preg_replace($reg, "$1<div class=\"$3\">$4</div>$5", $result, -1, $count);
    echo "step $step: $result\n";
    $step++;
} while ($count > 0);

这个输出:

step 0: a[div class="b"]c[div class="d"]e[/div]f[/div]g
step 1: a[div class="b"]c<div class="d">e[/div]f</div>g
step 2: a<div class="b">c<div class="d">e</div>f</div>g
step 3: a<div class="b">c<div class="d">e</div>f</div>g

注意:现在匹配一次太频繁了,循环不是最优的。

【讨论】:

  • 我试过了。它也不起作用......它仍然找不到第一个 bbcode 标记的匹配项。示例输出将是: [div class="class1"]
    div inside a div[/div]
  • 谢谢,您的回答很有帮助。但现在我认为它不能只用正则表达式来完成。我还查看了网络上的其他一些 bbcode 库.. 没有找到一个完整的...也尝试了 JBBcode,但我无法自定义我自己的一些标签...有什么建议吗?
  • 抱歉,我没有使用 BBcode et al 的经验,我无法推荐任何库。但是,真的有必要像 Q 中那样使用类构造吗?现在,您将 reg-exp 添加为数组键(因此,您不能复制它)。也许你可以使用另一种方法?一个简单的函数?
  • 我知道这是一个老问题,但如果有人仍在寻找解决方案,我的 repo 的 link 可能是您的解决方案。
猜你喜欢
  • 2012-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-03
  • 1970-01-01
  • 2012-06-13
相关资源
最近更新 更多