Mr. B
Mr. B

Reputation: 2787

Replace the Nth occurrence of char in a string with a new substring

I want to do a str_replace() but only at the Nth occurrence.

Inputs:

$originalString = "Hello world, what do you think of today's weather"; 
$findString = ' ';
$nthOccurrence = 8;
$newWord = ' beautiful ';

Desired Output:

Hello world, what do you think of today's beautiful weather

Upvotes: 1

Views: 866

Answers (5)

mickmackusa
mickmackusa

Reputation: 48101

Here is a tight little regex with \K that allows you to replace the nth occurrence of a string without repeating the needle in the pattern. If your search string is dynamic and might contain characters with special meaning, then preg_quote() is essential to the integrity of the pattern.

If you wanted to statically write the search string and nth occurrence into your pattern, it could be:

  • (?:.*?\K ){8}
  • or more efficiently for this particular case: (?:[^ ]*\K ){8}

\K tells the regex pattern to "forget" any previously matched characters in the fullstring match. In other words, "restart the fullstring match" or "Keep from here". In this case, the pattern only keeps the 8th space character.

Code: (Demo)

function replaceNth(string $input, string $find, string $replacement, int $nth = 1): string {
    $pattern = '/(?:.*?\K' . preg_quote($find, '/') . '){' . $nth . '}/';
    return preg_replace($pattern, $replacement, $input, 1);
}

echo replaceNth($originalString, $findString, $newWord, $nthOccurrence);
// Hello world, what do you think of today's beautiful weather

Another perspective on how to grapple the asked question is: "How to insert a new string after the nth instance of a search string?" Here is a non-regex approach that limits the explosions, prepends the new string to the last element then re-joins the elements. (Demo)

$originalString = "Hello world, what do you think of today's weather"; 
$findString = ' ';
$nthOccurrence = 8;
$newWord = 'beautiful ';   // notice that leading space was removed

function insertAfterNth($input, $find, $newString, $nth = 1) {
    $parts = explode($find, $input, $nth + 1);
    $parts[$nth] = $newString . $parts[$nth];
    return implode($find, $parts);
}

echo insertAfterNth($originalString, $findString, $newWord, $nthOccurrence);
// Hello world, what do you think of today's beautiful weather

Upvotes: 2

Lectan
Lectan

Reputation: 294

First explode a string by parts, then concatenate the parts together and with search string, but at specific number concatenate with replace string (numbers here start from 0 for convenience):

function str_replace_nth($search, $replace, $subject, $number = 0) {
    $parts = explode($search, $subject);
    $lastPartKey = array_key_last($parts);
    $result = '';
    foreach($parts as $key => $part) {
        $result .= $part;
        if($key != $lastPartKey) {
            if($key == $number) {
                $result .= $replace;
            } else {
                $result .= $search;
            }
        }
    }
    return $result;
}

Usage:

$originalString = "Hello world, what do you think of today's weather"; 
$findString = ' ';
$nthOccurrence = 7;
$newWord = ' beautiful ';
$result = str_replace_nth($findString, $newWord, $originalString, $nthOccurrence);

Upvotes: 0

StackSlave
StackSlave

Reputation: 10617

I would consider something like:

function replaceNth($string, $substring, $replacement, $nth = 1){
  $a = explode($substring, $string); $n = $nth-1;
  for($i=0,$l=count($a)-1; $i<$l; $i++){
    $a[$i] .= $i === $n ? $replacement : $substring;
  }
  return join('', $a);
}
$originalString = 'Hello world, what do you think of today\'s weather';
$test = replaceNth($originalString, ' ', ' beautiful ' , 8);
$test2 = replaceNth($originalString, 'today\'s', 'good');

Upvotes: 0

scofield.pan
scofield.pan

Reputation: 91

$originalString = "Hello world, what do you think of today's weather"; 
$findString = ' ';
$nthOccurrence = 8;
$newWord = ' beautiful ';

$array = str_split($originalString);
$count = 0;
$num = 0;
foreach ($array as $char) {
    if($findString == $char){
        $count++;
    }
    $num++;
    if($count == $nthOccurrence){
        array_splice( $array, $num, 0, $newWord );
        break;
    }
}
$newString = '';
foreach ($array as $char) {
    $newString .= $char;
}

echo $newString;

Upvotes: 0

Mr. B
Mr. B

Reputation: 2787

I found an answer here - https://gist.github.com/VijayaSankarN/0d180a09130424f3af97b17d276b72bd

$subject = "Hello world, what do you think of today's weather"; 
$search = ' ';
$occurrence = 8;
$replace = ' nasty ';

/**
 * String replace nth occurrence
 * 
 * @param type $search      Search string
 * @param type $replace     Replace string
 * @param type $subject     Source string
 * @param type $occurrence  Nth occurrence
 * @return type         Replaced string
 */
function str_replace_n($search, $replace, $subject, $occurrence)
{

    $search = preg_quote($search);
    echo preg_replace("/^((?:(?:.*?$search){".--$occurrence."}.*?))$search/", "$1$replace", $subject);
}

str_replace_n($search, $replace, $subject, $occurrence);

Upvotes: 0

Related Questions