Take the 2-minute tour ×

How can i count the number of words between two words?

   $txt = "tükörfúrógép banana orange lime, tükörfúrógép cherry árvíztűrő orange lyon
    cat lime mac tükörfúrógép cat orange lime cat árvíztűrő
    tükörfúrógép banana orange lime
    orange lime cat árvíztűrő";

The two words: 'árvíztűrő' and 'tükörfúrógép'
I need this return:
tükörfúrógép cherry árvíztűrő
tükörfúrógép cat orange lime cat árvíztűrő
tükörfúrógép banana orange lime orange lime cat árvíztűrő

Now i have this regular expression:

share|improve this question

3 Answers 3

up vote 7 down vote accepted

I have several things to point out:

  1. You can't do it in one regex. Regex is forward-only, reversed match order requires a second regex.
  2. You use (.*)?, but you mean (.*?)
  3. To aquire correct matches, you must ensure that the left boundary of your expression cannot occur in the middle.
  4. You should denote word boundaries (\b) around your delimiter words to ensure whole-word matches. EDIT: While this is correct in theory, it does not work for Unicode input in PHP.
  5. You should switch the PHP locale to Hungarian (it is Hungarian, right?) before calling preg_match_all(), because the locale has an influence on what's considered a word boundary in PHP. EDIT: The meaning of \b does in fact not change with the selected locale.

That being said, regex #1 is:


and regex #2 is analoguous, just with reversed delimiter words.

Regex explanation:

(               # match group 1:
  \b            #   a word boundary
  tükörfúrógép  #   your first delimiter word
  \b            #   a word boundary
)               # end match group 1
(               # match group 2:
  (?:           #   non-capturing group:
    (?!         #     look-ahead:
      \1        #       must not be followed by delimiter word 1
    )           #     end look-ahead
    .           #     match any next char (includes \n with the "s" switch)
  )*?           #   end non-capturing group, repeat as often as necessary
)               # end match group 2 (this is the one you look for)
\b              # a word boundary
árvíztűrő       # your second delimiter word
\b              # a word boundary

UPDATE: With PHP's patheticpoor Unicode string support, you will be forced to use expressions like these as replacements for \b:

$before = '(?<=^|[^\p{L}])';
$after  = '(?=[^\p{L}]|$)';

This suggestion has been taken from another question.

share|improve this answer
This return empty array: Array ( [0] => Array ( ) [1] => Array ( ) [2] => Array ( ) ) –  turbod Jul 21 '10 at 7:39
PS: Well, to be completely honest - you can do it in one regex, by concatenating regex #1 and regex #2 like this #1|#2. It's up to you if you consider the resulting expression worthwhile. ;-) –  Tomalak Jul 21 '10 at 7:43
@turbod: What does a simple \árvíztűrő\b give you? –  Tomalak Jul 21 '10 at 7:45
I'm currently researching the way \b works with PHP PCRE and unicode strings. Looks like the locale does not have an influence, and an alternative must be used for "international" word boundaries. When I found something, I'll update my answer. –  Tomalak Jul 21 '10 at 7:53
Thanks Tomalak! This expression is work! ((?<!\pL)tükörfúrógép(?!\pL))((?:(?!\1).)*?)(?<!\pL)árvíztűrő(?!\pL)|((?<!\pL)ár‌​víztűrő(?!\pL))((?:(?!\1).)*?)(?<!\pL)tükörfúrógép(?!\pL) –  turbod Jul 21 '10 at 8:02

To count words between two words you can easily use:

count(split(" ", "lime orange banana"));

And a function that returns an array with matches and counts will be:

function count_between_words($text, $first, $second, $case_sensitive = false)
    if(!preg_match_all('/('.$first.')((?:(?!\\1).)*?)'.$second.'/s' . ($case_sensitive ? "" : "i"), preg_replace("/\\s+/", " ", $text), $results, PREG_SET_ORDER))
        return array();

    $data = array();

    foreach($results as $result)
        $result[2] = trim($result[2]);
        $data[] = array("match" => $result[0], "words" => $result[2], "count" => count(split(" ", $result[2])));

    return $data;

$result = count_between_words($txt, "tükörfúrógép", "árvíztűrő");


Result will be:

    [0] => Array
        [match] => tükörfúrógép cherry árvíztűrő
        [words] => cherry
        [count] => 1

    [1] => Array
        [match] => tükörfúrógép cat orange lime cat árvíztűrő
        [words] => cat orange lime cat
        [count] => 4

    [2] => Array
        [match] => tükörfúrógép banana orange lime orange lime cat árvíztűrő
        [words] => banana orange lime orange lime cat
        [count] => 6
share|improve this answer
Thanks William! Is great! But what happens if you reverse the order of the parameters? For example: $result = count_between_words($txt, "árvíztűrő","tükörfúrógép"); –  turbod Jul 21 '10 at 8:09
Search the reverse is not a logic error, is a completely different search. Why? :o –  Wiliam Jul 21 '10 at 8:17
+1 for providing a self-contained solution. The regex however needs some improvement because it makes assumptions that may or may not be true (namely: \s* and [^,]+?) and can produce false negatives because of this. –  Tomalak Jul 21 '10 at 8:25
Reverse will return: " árvíztűrő orange lyon cat lime mac tükörfúrógép" (5) and "árvíztűrő tükörfúrógép" (0) –  Wiliam Jul 21 '10 at 8:25
Tomalak, I used \s* to trim the result contained in ([^,]+?) but you are right, seeing the example he gave us and thinking in a normal human redacted post this will be ok, errors can be easily fixed. With [^,] is the same point, in human redacted texts coma separates orations and if you don't use it in this example will return a false positive. (Ah! Thanks for the point!) –  Wiliam Jul 21 '10 at 8:29

Instead of a huge, confusing regexp, why not write a few lines using various string functions?


$start = strpos($txt, 'árvíztűrő') + 9; // position of first char after 'árvíztűrő'
$end   = strpos($txt, 'tükörfúrógép', $start);
$inner = substr($txt, $start, $end - $start);
$words = preg_split("/[\s,]+/", $inner);
$num   = count($words);

Of course, this will eat up memory if you have some gigantic input string...

share|improve this answer
Sorry, but this not work. –  turbod Jul 21 '10 at 8:03
Ah - what did it do? Looking at it now, a possible problem that comes to mind is that your funny accented characters probably aren't in the ASCII set and so the length of 'árvíztűrő' may be more than 9... –  Kelsey Rider Jul 21 '10 at 10:11

Your Answer


