PHP数组str_replace整个Word

I'm doing str_replace on a very long string and my $search is an array.

$search = array(
    " tag_name_item ",
    " tag_name_item_category "
);

$replace = array(
    " tag_name_item{$suffix} ",
    " tag_name_item_category{$suffix} "
);

echo str_replace($search, $replace, $my_really_long_string);

The reason why I added spaces on both $search and $replace is because I want to only match whole words. As you would have guessed from my code above, if I removed the spaces and my really long string is:

...
tag_name_item ...
tag_name_item_category ...
...

Then I would get something like

...
tag_name_item_sfx ...
tag_name_item_sfx_category ...
...

This is wrong because I want the following result:

...
tag_name_item_sfx ...
tag_name_item_category_sfx ...
...

So what's wrong?

Nothing really, it works. But I don't like it. Looks dirty, not well coded, inefficient.

I realized I can do something like this using regular expressions using the \b modifier but I'm not good with regex and so I don't know how to preg_replace.

A possible approach using regular expressions would/could look like this:

$result = preg_replace(
    '/\b(tag_name_item(_category)?)\b/',
    '$1' . $suffix,
    $string
);

How it works:

  • \b: As you say are word boundaries, this is to ensure we're only matching words, not word parts
  • (: We want to use part of our match in the replacement string (tag_name_index has to be replaced with itself + a suffix). That's why we use a match group, so we can refer back to the match in the replacement string
  • tag_name_index is a literal match for that string.
  • (_category)?: Another literal match, grouped and made optional through use of the ? operator. This ensures that we're matching both tag_name_item and tag_name_item_category
  • ): end of the first group (the optional _category match is the second group). This group, essentially, holds the entire match we're going to replace
  • \b: word boundary again

These matches are replaced with '$1' . $suffix. The $1 is a reference to the first match group (everything inside the outer brackets in the expression). You could refer to the second group using $2, but we're not interested in that group right now.

That's all there is to it really


More generic:

So, you're trying to suffix all strings starting with tag_name, which judging by your example, can be followed by any number of snake_cased words. A more generic regex for that would look something like this:

$result = preg_replace(
    '/\b(tag_name[a-z_]*)\b/',
    '$1' . $suffix,
    $string
);

Like before, the use of \b, () and the tag_name literal remains the same. what changed is this:

  • [a-z_]*: This is a character class. It matches characters a-z (a to z), and underscores zero or more times (*). It matches _item and _item_category, just as it would match _foo_bar_zar_fefe.

These regex's are case-sensitive, if you want to match things like tag_name_XYZ, you'll probably want to use the i flag (case-insensitive): /\b(tag_name[a-z_]*)\b/i

Like before, the entire match is grouped, and used in the replacement string, to which we add $suffix, whatever that might be

To avoid the problem, you can use strtr that parses the string only once and chooses the longest match:

$pairs = [ " tag_name_item "          => " tag_name_item{$suffix} ",
           " tag_name_item_category " => " tag_name_item_category{$suffix} " ];

$result = strtr($str, $pairs);

This function replaces the entire whole word but not the substring with an array element which matches the word

<?PHP
      function removePrepositions($text){

            $propositions=array('/\b,\b/i','/\bthe\b/i','/\bor\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }    

?>

See the entire example