6

If you had the string

'Old string Old more string Old some more string'

and you wanted to get

'New1 string New2 more string New3 some more string'

how would you do it?

In other words, you need to replace all instances of 'Old' with variable string 'New'.$i. How can it be done?

5 Answers 5

6

An iterative solution that doesn't need regular expressions:

$str = 'Old string Old more string Old some more string';
$old = 'Old';
$new = 'New';

$i = 1;

$tmpOldStrLength = strlen($old);

while (($offset = strpos($str, $old, $offset)) !== false) {
  $str = substr_replace($str, $new . ($i++), $offset, $tmpOldStrLength);
}

$offset in strpos() is just a little bit micro-optimization. I don't know, if it's worth it (in fact I don't even know, if it changes anything), but the idea is that we don't need to search for $old in the substring that is already processed.

See Demo

Old string Old more string Old some more string
New1 string New2 more string New3 some more string
Sign up to request clarification or add additional context in comments.

4 Comments

That's no optimization, the function won't work w/o it. You need to specify the offset replacement starts at.
Thats wrong (and I tested it; it were my first suggestion): The $offset in strpos() is optional. Because $old is replaced from left to right strpos() always returns the index of the left-most occurence of $old, which will move forward with every replacement. The $offset here should just avoid, that we are looking for $old in the parts of the string, that cannot contain $old anymore.
@KingCrunch: I was referring to $offset for substr_replace(), my bad read. For strpos, I think it's a good idea, especially for long strings.
@Marko: I've added a function below that can be used with callbacks and multiple replacements.
2

Use preg_replace_callback.

$count = 0;
$str = preg_replace_callback(
    '~Old~',
    create_function('$matches', 'return "New".$count++;'),
    $str
);

3 Comments

Your anonymous function will not have access to $count here.
What anubhava wants to say: It doesn't work (and please don't "fix" it with global variables :X)
Ok, I see. Thanks KingCrunch, then I vote for your iterative solution as well.
1

From the PHP manual on str_replace:

Replace all occurrences of the search string with the replacement string

mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

search

The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles.

replace

The replacement value that replaces found search values. An array may be used to designate multiple replacements.

subject

The string or array being searched and replaced on, otherwise known as the haystack.

If subject is an array, then the search and replace is performed with every entry of subject, and the return value is an array as well.

count

If passed, this will be set to the number of replacements performed.

3 Comments

This answer isn't wrong per se. You just need to compute the variables up front of the replace instead of on the fly in a callback function.
But it cannot handle the situation, where one $search should be replaced by different $replace depending on the number of occurence. According the question: It will always replace every occurence of Old with the same string.
@KingCrunch: Ah, true. Was formulating a demo when I realised this.
1

Use:

$str = 'Old string Old more string Old some more string';
$i = 1;
while (preg_match('/Old/', $str)) {
    $str = preg_replace('/Old/', 'New'.$i++, $str, 1);
}
echo $str,"\n";

Output:

New1 string New2 more string New3 some more string

Comments

1

I had some similar solution like KingCrunch's, but as he already answered it, I was wondering about a str_replace variant with a callback for replacements and came up with this (Demo):

$subject = array('OldOldOld', 'Old string Old more string Old some more string');
$search = array('Old', 'string');
$replace = array(
    function($found, $count) {return 'New'.$count;},
    function($found, $count) {static $c=0; return 'String'.(++$c);}
);
$replace = array();

print_r(str_ureplace($search, $replace, $subject));

/**
 * str_ureplace
 *
 * str_replace like function with callback
 *
 * @param string|array search
 * @param callback|array $replace
 * @param string|array $subject
 * @param int $replace_count
 * @return string|array subject with replaces, FALSE on error.
 */
function str_ureplace($search, $replace, $subject, &$replace_count = null) {
    $replace_count = 0;

    // Validate input
    $search = array_values((array) $search);
    $searchCount = count($search);
    if (!$searchCount) {
        return $subject;
    }
    foreach($search as &$v) {
        $v = (string) $v;
    }
    unset($v);
    $replaceSingle = is_callable($replace);
    $replace = $replaceSingle ? array($replace) : array_values((array) $replace);
    foreach($replace as $index=>$callback) {
        if (!is_callable($callback)) {
            throw new Exception(sprintf('Unable to use %s (#%d) as a callback', gettype($callback), $index));
        }
    }

    // Search and replace
    $subjectIsString = is_string($subject);
    $subject = (array) $subject;
    foreach($subject as &$haystack) {
        if (!is_string($haystack)) continue;
        foreach($search as $key => $needle) {
            if (!$len = strlen($needle))
                continue;
            $replaceSingle && $key = 0;
            $pos = 0;
            while(false !== $pos = strpos($haystack, $needle, $pos)) {
                $replaceWith = isset($replace[$key]) ? call_user_func($replace[$key], $needle, ++$replace_count) : '';
                $haystack = substr_replace($haystack, $replaceWith, $pos, $len);
            }
        }
    }
    unset($haystack);

    return $subjectIsString ? reset($subject) : $subject;
}

3 Comments

You don't need to distinguish between, if $search is a string, or an array: codepad.org/H4MZCiLd However, I think it may be useful.
@KingCrunch: Thanks for the notice. I run over it as well while boiling it up. Just updated to a new version that takes array as any argument to better match PHP's behavior, even does now return false on error and gives notices in case parameters were wrong (it might check for valid callback though, that's still missing).
Another update with more similar between behaviour compared to str_replace. Now throws exception where str_replace would do a fatal error, and the replacement is now similar to how str_replace does it (reg. number of replacements).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.