-1

So once again I'm practicing PHP. Specifically strpos() in a while loop.

The trouble with the below code is that strpos() results in 0 at the first loop, which yields a false result in the while condition, thus terminating the loop.

$string = 'orem ipsum dolor sit amet, consectetur adipisicing elit.';
$find = 'o';
$offset = 0;
$length  = strlen($find);

while ($string_pos = strpos($string, $find, $offset)) {
    echo 'String '.$find.' found at position '.$string_pos.'.<br>';
    $offset = $length + $string_pos;
}

I'm quite new to all of this, could someone help me with an explanation and a solution? I'm looking for it to loop all occurrences.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Russ
  • 469
  • 1
  • 7
  • 13
  • 2
    Possible duplicate of [The 3 different equals](http://stackoverflow.com/questions/2063480/the-3-different-equals) – Jeff Lambert Feb 18 '15 at 19:09
  • 2
    you can probably fix your loop if you say something like this instead: `while (($string_pos = strpos($string, $find, $offset)) !== false)` – Jeff Lambert Feb 18 '15 at 19:09
  • If you look at the documentation, it states and shows an example of using === or !== to avoid having the value of 0 being cast to false. – kainaw Feb 18 '15 at 19:20
  • The !== false works however I'm not really understanding why. Can't get my head around it, how does the 'does not equal false' rectify this problem? Does it act as an essential if statement? Like 'if 0 return 0 not false'. I know I'm being dense but it's just not seeming to 'click' for me. – Russ Feb 18 '15 at 19:32
  • `!==` works because `0` is considered as `false` by `while`. So by `!==` we are forcing `strpos` to check exactly for `false`. Hope that is clear. – Jigar Feb 18 '15 at 19:35

2 Answers2

0

If you don't want to use strpos():

<?php

$string = 'orem ipsum dolor sit amet, consectetur adipisicing elit.';
$find = 'o';

for($i = 0; $i <= strlen($string)-1; $i++){
    // we are checking with each index of the string here
    if($string[$i] == $find){
        echo 'String '.$find.' found at position '.$i.'.<br>';
    }
}

?>
Jigar
  • 3,256
  • 1
  • 30
  • 51
0

I'm not a big fan of the "iterate every character" answer from Jigar because it doesn't provide a quick exit when there are no more needles found (it iterates the entire string regardless) -- this can become more costly in longer strings. Imagine you have a 10,000 character string and the only occurrence of the needle is on the first character -- this would mean doing 9999 more iterated checks for no usable output. The truth is I didn't do any benchmarking, it may not be a big deal at all.

As for your method, you merely need to perform a strict comparison on the result of strpos() so that php will correctly differentiate between a false and a 0 result. To accomplish this, you only need to wrap your strpos() declaration in parentheses and write a type-specific comparision (!==false).

Here are two other ways (a non-regex and a regex):

Code: (Demo)

$string='orem ipsum dolor sit amet, consectetur adipisicing elit.';
$find='o';
$offset=0;
$length=strlen($find);

while(($string_pos=strpos($string,$find,$offset))!==false){  // just use a strict comparison
    echo "String $find found at position $string_pos\n";
    $offset=$length+$string_pos;
}

echo "\n";
var_export(preg_match_all('/o/',$string,$out,PREG_OFFSET_CAPTURE)?array_column($out[0],1):'no matches');

Output:

String o found at position 0
String o found at position 12
String o found at position 14
String o found at position 28

array (
  0 => 0,
  1 => 12,
  2 => 14,
  3 => 28,
)

For your case, preg_match_all() all is overkill. However, if you want to count multiple different words, or whole words, or something otherwise tricky it may be the right tool.

Beyond this, depending on the searching scenario, str_word_count() has a setting where it can return the offsets of all words in a string -- then you could call a filtering function to only retain your desired word(s). Just thought I'd throw this suggestion in for future readers; it doesn't apply for this question.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136