Search code examples
phpregexpreg-match

Match, not having a word right before a certain word


I would like to match the word 'mice' without any words right before it, but my regex isn't working. What am I doing wrong?

<?php

$string1 = "one mice";
$string2 = "one .mice";
$string3 = "mice";

if (preg_match("~(?<![a-z]+ )\bmice~", $string1)) { echo "1"; }
if (preg_match("~(?<![a-z]+ )\bmice~", $string2)) { echo "2"; }
if (preg_match("~(?<![a-z]+ )\bmice~", $string3)) { echo "3"; }

?>

Expected Result:

23

Actual Result:

null

I expect it echo 2 because instead of having a word before 'mice', it's a period instead. And I expect it to echo 3 because there's no word before the word 'mice'. What am I doing wrong?


Solution

  • You can use negative lookbehind and make the dot optional, i.e.:

    if (preg_match('/(?<![a-z] )\.?\bmice\b/i', $subject)) {
        # Successful match
    }
    

    Regex explanation:

    (?<![a-z] )\.?\bmice\b
    
    Options: Case insensitive
    
    Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind) «(?<![a-z] )»
       Match a single character in the range between “a” and “z” (case insensitive) «[a-z]»
       Match the character “ ” literally « »
    Match the character “.” literally «\.?»
       Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
    Assert position at a word boundary (position preceded or followed—but not both—by a Unicode letter, digit, or underscore) «\b»
    Match the character string “mice” literally (case insensitive) «mice»
    Assert position at a word boundary (position preceded or followed—but not both—by a Unicode letter, digit, or underscore) «\b»
    

    Regex101 Demo


    Ideone Demo


    Note:

    1. PHP preg >= 5.6.11 does not support infinite repetition inside lookbehind (?<![a-z]+ )