1

Let's say I have a string

This $0 is $2 $1. Still, \$$3 is a lot to pay for a puppy.

and an array of replacements

array('puppy', 'cute', 'ridiculously', '1300')

What would be the cleanest way to replace the tokens in the string with the corresponding items in the array, letting me backslash-escape the token character (in this case $)? I also want to replace unmatched tokens with the empty string.

s4y
  • 50,525
  • 12
  • 70
  • 98

5 Answers5

4
foreach ($array AS $key => $value)
{
    $string = str_replace('$' . $key, $value, $string);
}
Josh Leitzel
  • 15,089
  • 13
  • 59
  • 76
  • Thanks. This is a nice and simple solution, but it doesn't handle two cases: escaping literal `$`s in the string, and unmatched tokens (leaving them in the string is a valid, but I would rather remove them). I'll update the question to clarify. – s4y Oct 25 '09 at 02:10
  • doesn't respect backslash-escaping – Jason S Oct 25 '09 at 02:11
  • 1
    Thanks to whoever downvoted me. FWIW, the original question (the one I was answering) made no mention of backslash escaping. – Josh Leitzel Oct 25 '09 at 03:30
1
$test_sub= 'This $0 is $2 $1. Still, \$$3 is a lot to pay for a puppy.';
$GLOBALS['replacers'] = array('puppy', 'cute', 'ridiculously', '1300');
echo preg_replace_callback('/[^\$]\$([0-9])+/',
    create_function(
        '$matches',
        'return $matches[0][0] . $GLOBALS[\'replacers\'][$matches[1]];'
    ),
    $test_sub
);

This is a POC of how it can be done. A simple regular expression and a callback for replacement. The actual implementation varies on what exactly you want to do with it. I hope it helped.

Kon Pal
  • 546
  • 1
  • 3
  • 13
1

i guess you meant "\$3", not "\$$3"

preg_replace('~(?<!\\\\)\$(\d+)~e', 'isset($array[$1])?$array[$1]:""', $source);

btw, did you know that sprintf allows numbered params as well (http://php.net/manual/en/function.sprintf.php example 3)

user187291
  • 53,363
  • 19
  • 95
  • 127
  • 1
    i interpreted \$$3 as escaping the $ and then reading the $3 variable as $1300. i don't think it was a typo. – Brandon Henry Oct 25 '09 at 04:18
  • cletus' response is the most complete, but I can't argue with a one-liner. Needs a quick pass with a second `preg_replace` to remove backslashes from escaped `$`. – s4y Oct 31 '09 at 21:51
1

Here's one version.

$replacements = array('puppy', 'cute', 'ridiculously', '1300');
$input = 'This $0 is $2 $1. Still, \$3 is a lot to pay for a puppy.';

$output = preg_replace_callback('/(?<!\\\\)\$(\d+)/', 'replace_input', $input);

echo $input . "<br>";
echo $output;

function replace_input($matches) {
  global $replacements;
  $index = $matches[1];
  return $index < 0 || $index >= count($replacements) ? $matches[0] : $replacements[$index];
}

Output:

This $0 is $2 $1. Still, \$3 is a lot to pay for a puppy.
This puppy is ridiculously cute. Still, \$3 is a lot to pay for a puppy.

It handles a backslash before the $ to escape that variable. That could be an awkward syntax because then you need to escape backslashes, which complicates it further (and isn't handled in this case). The regular expression basically means $ followed by one or more digits as long as the $ isn't preceded by a backslash (using a negative lookbehind).

It uses a global for the replacements array. There are two alternatives to this:

  1. Use a closure (requires PHP 5.3+); or
  2. Use create_function().

But I think the global is simpler and "global" unless you have a good reason to do something different in spite of the usual distaste we have for such things.

cletus
  • 616,129
  • 168
  • 910
  • 942
0

This solution follows the question and borrows a little from Josh Leitzel's answer. Unmatched patterns (eg. $4, $5) are replaced by an empty string, removing them from the input.

$input = "This $0 is $2 $1. Still, \$$3 is a lot to pay for a puppy.";
$replacements = array('puppy','cute','ridiculously','1300');
$pattern = "/[$]{1}([0-9]{1})/";

preg_match_all($pattern, $input, $matches);

if (isset($matches[1]))
 foreach ($matches[1] as $key => $value)
 {
  $input = str_replace("$".$value, $replacements[$value], $input);
 }

echo $input;

Output:

This puppy is ridiculously cute. Still, $1300 is a lot to pay for a puppy.

Tramov
  • 1,166
  • 1
  • 11
  • 17