There must be a fast and efficient way to split a (text) string at the "nth" occurrence of a needle, but I cannot find it. There is a fairly full set of functions in the [strpos comments in the PHP manual](http://www.php.net/manual/en/function.strpos.php#102336), but that seems a bit much for what I need.
I have plain text as `$string`, and want to split it at *nth* occurrence of `$needle`, and in my case, `needle` is simply a space. (I can do the sanity checks!)
Can someone point me in the right direction? Many thanks!
Could be?
function split2($string,$needle,$nth){
$max = strlen($string);
$n = 0;
for($i=0;$i<$max;$i++){
if($string[$i]==$needle){
$n++;
if($n>=$nth){
break;
}
}
}
$arr[] = substr($string,0,$i);
$arr[] = substr($string,$i+1,$max);
return $arr;
}
Answer #2
Jack Douglas
You can use recursion to do this too:
```
function strposn(string $haystack, string $needle, $nth, $offset = 0){ return $nth?strposn($haystack,$needle,$nth-1,strpos($haystack,$needle,$offset)+1):$offset; }
$my_string = 'one two three four five six';
$split_pos = strposn($my_string,' ',4);
echo 'left: "'.substr($my_string,0,$split_pos-1).'"'.PHP_EOL;
echo 'right: "'.substr($my_string,$split_pos).'"'.PHP_EOL;
```
produces:
```none
left: "one two three four"
right: "five six"
```
If you care what happens when passing in either 0 or a number higher than the actual number of occurrences you may need to add some checking.
I really like Hamze GhaemPanah's [answer][1] for its brevity. However, there's a small bug in it.
In the original code:
```
$i = $pos = 0;
do {
$pos = strpos( $string, $needle, $pos+1 );
} while( $i++ < $nth);
```
`$nth` in the `do while` loop should be replaced with `($nth-1)` as it will incorrectly iterate one extra time - setting the `$pos` to the position of the `$nth+1` instance of the needle. Here's an example [playground to demonstrate][2]. If this link fails here is the code:
```
$nth = 2;
$string = "44 E conway ave west horse";
$needle = " ";
echo"======= ORIGINAL =======\n";
$i = $pos = 0;
do {
$pos = strpos( $string, $needle, $pos+1 );
} while( $i++ < $nth);
echo "position: $pos \n";
echo substr($string, 0, $pos) . "\n\n";
/*
Outputs:
======= ORIGINAL =======
position: 11
44 E conway
*/
echo"======= FIXED =======\n";
$i = $pos = 0;
do {
$pos = strpos( $string, $needle, $pos+1 );
} while( $i++ < ($nth-1) );
echo "position: $pos \n";
echo substr($string, 0, $pos);
/*
Outputs:
======= FIXED =======
position: 4
44 E
*/
```
That is, when searching for the position of the 2nd instance of our needle, our loop iterates one extra time setting `$pos` to the position of our 3rd instance of our needle. So, when we split the string on the 2nd instance of our needle - as the OP asked - we get the incorrect substring.
Kudos to Hamze for the clever answer.
[1]: /php?q=1071#a1290
[2]: https://www.tehplayground.com/vAutCoJvXvqRBWAu
Easily, just do it: (updated!)
$i = $pos = 0;
do {
$pos = strpos( $string, $needle, $pos+1 );
} while( ++$i < $nth);
If your needle will always be 1 character, use Galled's answer, it's going to be faster by quite a bit. If your $needle is a string, try this. Seems to work fine.
function splitn($string, $needle, $offset)
{
$newString = $string;
$totalPos = 0;
$length = strlen($needle);
for($i = 0; $i < $offset; $i++)
{
$pos = strpos($newString, $needle);
// If you run out of string before you find all your needles
if($pos === false)
return false;
$newString = substr($newString, $pos+$length);
$totalPos += $pos+$length;
}
return array(substr($string, 0, $totalPos-$length),substr($string, $totalPos));
}
Taking [Matthew's answer][1] and adding a solution for [Dɑvïd's comment][2]:
function split_nth($str, $delim, $n) {
$result = array_map(function($p) use ($delim) {
return implode($delim, $p);
}, array_chunk(explode($delim, $str), $n));
$result_before_split = array_shift($result);
$result_after_split = implode(" ", $result);
return array($result_before_split, $result_after_split);
}
Just call it by:
list($split_before, $split_after) = split_nth("1 2 3 4 5 6", " ", 2);
Output:
1 2
3 4 5 6
[1]: https://stackoverflow.com/a/5956463/4829915
[2]: https://stackoverflow.com/questions/5956066/how-to-split-a-string-in-php-at-nth-occurrence-of-needle/5956463#comment6869588_5956463