PHP includes some handy functions to find the first or last occurrence of a given string token in a string: strpos and strrpos. However these functions are limited to just the first occurrence; what if I want to know the location of the second token’s position, or the third? These problems usually result in some serious coding acrobatics.
Well no need for code-jitsu anymore. Based almost completely on a post I found at another blog — which is now down, how’s that for timing? — here are two functions which allow you to search for any occurrence of a specific token in a string…
/**
* Find position of Nth $occurrence of $needle in $haystack
* Starts from the beginning of the string
**/
function strpos_offset($needle, $haystack, $occurrence) {
// explode the haystack
$arr = explode($needle, $haystack);
// check the needle is not out of bounds
switch( $occurrence ) {
case $occurrence == 0:
return false;
case $occurrence > max(array_keys($arr)):
return false;
default:
return strlen(implode($needle, array_slice($arr, 0, $occurrence)));
}
}
/**
* Find position of Nth $occurrence of $needle in $haystack
* Starts from the end of the string
**/
function strrpos_offset($needle, $haystack, $occurrence) {
// explode the haystack
$arr = array_reverse(explode($needle, $haystack));
// check the needle is not out of bounds
switch( $occurrence ) {
case $occurrence == 0:
return false;
case $occurrence > max(array_keys($arr)):
return false;
default:
$inverted = strlen(implode($needle, array_slice($arr, 0, $occurrence)));
$actual = (strlen($haystack) - 1) - $inverted;
return $actual;
}
}
// look for second occurrence of letter 'a' from the start of string
echo strpos_offset('a', 'abracadabra', 2);
// returns 3
// look for second occurrence of letter 'a' from the end of string
echo strrpos_offset('a', 'abracadabra', 2);
// returns 7
In terms of use, we’ve essentially added an extra argument to strpos and strrpos that specifies which occurrence you’re looking for. In other words, you can make both of these functions work like the PHP standards by setting the third $occurrence variable to 1.
Very good post! Extremely helpful. You just saved my word wrapping script…
This is one of those problems that is SCREAMING for a recursive solution. Performance-wise this is way faster compared to exploding strings, imploding and slicing arrays and such:
function strpos_offset_recursive($needle,$haystack,$occurence){
if(($o=strpos($haystack,$neelde))===false) return false;
if($occurence>1){
$found=strpos_offset_recursive($needle,substr($haystack,$o+strlen($needle)),$occurence-1);
return ($found!==false)?$o+$found+strlen($needle):false;
}
return $o;
}
Thanks Patrick you rock! I tested both scripts and the recursive way is indeed faster.