PHP preg_replace function replacing previous match

2019-05-09 23:21发布

问题:

I have a fairly basic situation, I have an array of strings and I want to find all matches for these in a single string and put strong tags around them. This is what I have so far:

$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);

The problem is that the preg_replace function seems to be matching the "s" search term against the strong tags as they're replaced. So I'm ending up with:

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

When what I really want is:

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

So, can you guys suggest where I'm going wrong?

Any help is very much appreciated, I'm tearing my hair out over this and I must be close.

回答1:

You don't want to do three replacements, but one:

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

hakre's edit: This works like this or that or that string. The first match will be taken. So start with the longest string and take the smallest string as the last one.

binary's answer on this edit: see comments


Full version:

<?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);


回答2:

The simplest ways to ensure this does not happen would be to use two special "faketags" that don't risk being replaced, and then replace those, or specify that the words must be a full word match and begin and end at a word boundary.

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

or

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

The second is more elegant (and faster - also speeds up the search, I bet) but prevents you from using partial word replaces.