Using str_replace so that it only acts on the first match?

Viewed 305425

I want a version of str_replace() that only replaces the first occurrence of $search in the $subject. Is there an easy solution to this, or do I need a hacky solution?

23 Answers

There's no version of it, but the solution isn't hacky at all.

$pos = strpos($haystack, $needle);
if ($pos !== false) {
    $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
}

Pretty easy, and saves the performance penalty of regular expressions.


Bonus: If you want to replace last occurrence, just use strrpos in place of strpos.

Can be done with preg_replace:

function str_replace_first($search, $replace, $subject)
{
    $search = '/'.preg_quote($search, '/').'/';
    return preg_replace($search, $replace, $subject, 1);
}

echo str_replace_first('abc', '123', 'abcdef abcdef abcdef'); 
// outputs '123def abcdef abcdef'

The magic is in the optional fourth parameter [Limit]. From the documentation:

[Limit] - The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit).


Though, see zombat's answer for a more efficient method (roughly, 3-4x faster).

Unfortunately, I don't know of any PHP function which can do this.
You can roll your own fairly easily like this:

function replace_first($find, $replace, $subject) {
    // stolen from the comments at PHP.net/str_replace
    // Splits $subject into an array of 2 items by $find,
    // and then joins the array with $replace
    return implode($replace, explode($find, $subject, 2));
}
$str = "/property/details&id=202&test=123#tab-6p";
$position = strpos($str,"&");
echo substr_replace($str,"?",$position,1);

Using substr_replace we can replace the occurrence of first character only in string. as & is repeated multiple times but only at first position we have to replace & with ?

The easiest way would be to use regular expression.

The other way is to find the position of the string with strpos() and then an substr_replace()

But i would really go for the RegExp.

According to my test result, I'd like to vote the regular_express one provided by karim79. (I don't have enough reputation to vote it now!)

The solution from zombat uses too many function calls, I even simplify the codes. I'm using PHP 5.4 to run both solutions for 100,000 times, and here's the result:

$str = 'Hello abc, have a nice day abc! abc!';
$pos = strpos($str, 'abc');
$str = substr_replace($str, '123', $pos, 3);

==> 1.85 sec

$str = 'Hello abc, have a nice day abc! abc!';
$str = preg_replace('/abc/', '123', $str, 1);

==> 1.35 sec

As you can see. The performance of preg_replace is not so bad as many people think. So I'd suggest the classy solution if your regular express is not complicated.

For Loop Solution

<?php
echo replaceFirstMatchedChar("&", "?", "/property/details&id=202&test=123#tab-6");

function replaceFirstMatchedChar($searchChar, $replaceChar, $str)
{
    for ($i = 0; $i < strlen($str); $i++) {

        if ($str[$i] == $searchChar) {
            $str[$i] = $replaceChar;
            break;
        }
    }
    return $str;
}

I would use preg instead. It has a LIMIT parameter you can set it to 1

preg_replace (regex, subst, string, limit) // default is -1
$str = "Hello there folks!"
$str_ex = explode("there, $str, 2);   //explodes $string just twice
                                      //outputs: array ("Hello ", " folks")
$str_final = implode("", $str_ex);    // glues above array together
                                      // outputs: str("Hello  folks")

There is one more additional space but it didnt matter as it was for backgound script in my case.

If you string does not contains any multibyte characters and if you want to replace only one char you can simply use strpos

Here a function who handle errors

/**
 * Replace the first occurence of given string
 *
 * @param  string $search  a char to search in `$subject`
 * @param  string $replace a char to replace in `$subject`
 * @param  string $subject
 * @return string
 *
 * @throws InvalidArgumentException if `$search` or `$replace` are invalid or if `$subject` is a multibytes string
 */
function str_replace_first(string $search , string $replace , string $subject) : string {
    // check params
    if(strlen($replace) != 1 || strlen($search) != 1) {
        throw new InvalidArgumentException('$search & $replace must be char');
    }elseif(mb_strlen($subject) != strlen($subject)){
        throw new InvalidArgumentException('$subject is an multibytes string');
    }
    // search 
    $pos = strpos($subject, $search);
    if($pos === false) {
        // not found
        return $subject;
    }

    // replace
    $subject[$replace] = $subject;

    return $subject;
}
Related