PHP preg_replace函数替换先前的匹配结果

3

我有一个相对简单的情况,我有一个字符串数组,我想要在另一个字符串中找到所有匹配项,并在它们周围加上标签。这是我目前的代码:

$searchWords = array("test","this","s");

for($i=0;$i<sizeof($searchWords);$i++) {
    $searchWords[$i] = "/".preg_quote($searchWords[$i])."/i";
}

$label = "This is a test string.";

$result = preg_replace($searchWords, "<strong>$0</strong>", $label);

echo($result);

问题在于preg_replace函数似乎将"s"搜索项与标签匹配并替换。所以最后得到的结果是:

<strong>Thisstrong> is a <strong>teststrong>.

而我真正想要的是:

<strong>This</strong> i<strong>s</strong> a <strong>test</strong>.

所以,你们能告诉我问题出在哪里吗?

非常感谢任何帮助,我已经因为这个问题快抓狂了,但我一定很接近解决方法。

2个回答

3

您不想进行三次替换,而是一次:

$result = preg_replace("#" . implode($searchWords, "|") . "#", "<strong>$0</strong>", $label);

完整版本:

<?php
$searchWords = array("t", "test", "this");
usort($searchWords, function ($a, $b) { return strlen($b) - strlen($a); });

foreach ($searchWords as &$word)
{
    $word = preg_quote($word);
}
unset($word);

$label = "This is a test string.";

$searchWords = implode($searchWords, "|");
$result = preg_replace("#{$searchWords}#i", "<strong>$0</strong>", $label);

echo($result);

@hakre,默认情况下它不会使用最大的字符串吗?除非你使用“ungreedy”标志,否则正则表达式不是这样工作的吗? - binaryLV
贪心算法仅适用于重复,这里没有重复。您可以考虑使用 array_map 来处理 preg_quote - hakre
@hakre,所以 preg_replace("#s|tests#i", "some tests", '*$0*') 应该返回 *s*ome te*s*t*s*,对吗?因为模式中的第一个字符串是s而不是tests。但是,在5.3.14上它返回 *s*ome *tests*。如果我在模式中交换stests,它也会做同样的事情。 - binaryLV
1
尝试使用“tome tests”和“(t|tests)i”进行测试。你的看起来是第一个匹配第一个,与长度无关,因此不符合测试要求。 - hakre
太棒了,这个很有效。谢谢大家。有趣的是,无论字符串的顺序如何,它似乎都能完美地运行。 - adc124
我已经添加了 usort() 来对 $searchWords 进行排序。@adc124,你也应该在你的代码中包含它。 - binaryLV

0
最简单的方法是使用两个特殊的“伪标签”,这样就不会被替换,然后替换它们,或指定这些词必须完全匹配,并且以单词边界开头和结尾,以确保这种情况不会发生。
  $result = preg_replace($searchWords, "<_^_>$0</_^_>", $label);
  $result = str_replace('_^_', 'strong', $result);

或者

  $searchWords[$i] = '/\b'.preg_quote($searchWords[$i]).'\b/i';

第二种方法更加优雅(也更快 - 我敢打赌它也会加速搜索),但是防止您使用部分单词替换。

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