代码之家  ›  专栏  ›  技术社区  ›  Giacomo1968

为什么我在PHP中匹配单词的正则表达式不能按预期工作?否定lookahead上的范围限定符正在取消意外输入字符串的资格

  •  0
  • Giacomo1968  · 技术社区  · 10 月前

    我使用的是PHP 7.4和PHP 8.2,我在PHP中使用了一个正则表达式来匹配单词(名称)。老实说,我几乎认不出我创建的这个regex怪物。因此,这个问题需要帮助来解决。基本上是这样的:

    $is_word = preg_match('/^(?![aeiou]{3,})(?:\D(?![^aeiou]{4,}[aeiou]*)(?![aeiou]{4,})){3,}$/i', $name);
    

    我已经用它来匹配我创建的脚本中的名称大约6年多了:它基本上会返回一个布尔值 TRUE FALSE 如果它与单词模式匹配。

    但今天它回来了 false 应视为有效的两个名称:

    • Drantch

    要对此进行测试,您可以使用以下一批测试名称;例如使用伪名称:

    • Nartinez
    • Drantch
    • Dratch
    • Xtmnprwq
    • 耶伦德斯
    • 博尔德伯格
    • 叶列诺维奇
    • 阿拉什
    • 默罕默德

    我试图调整正则表达式以设置第二个 {x,x} {5,}

    $is_word = preg_match('/^(?![aeiou]{3,})(?:\D(?![^aeiou]{5,}[aeiou]*)(?![aeiou]{4,})){3,}$/i', $name);
    

    它在匹配Drantch这样的名字的情况下起到了帮助作用,但它仍然完全错过了像Li这样的两个字母的名字。

    如何调整此正则表达式以正确匹配所有名称?如果不是所有的名字,怎么能调整到合适的匹配Drantch和其他明显的名字,除了李。

    注意,Xtmnprwq是一个假测试名称,所以我可以测试阴性和阳性。

    3 回复  |  直到 10 月前
        1
  •  1
  •   Barmar    10 月前

    您的regexp对单词有以下限制:

    • ^(?![aeiou]{3,}) -不能以3个或更多连续元音开头
    • (?![^aeiou]{4,} -中间不能有4个或4个以上连续的辅音
    • (?![aeiou]{4,}) -中间不能有4个或4个以上连续的元音
    • {3,} -长度必须至少为3个字符

    Li 违反了3个字符的要求。

    Drantch 违反了4个连续辅音的限制。

    调整或删除regexp的这些位,以更改允许这些名称的限制。

        2
  •  0
  •   Eugene Kaurov    10 月前

    要了解你在做什么,可以随意使用视觉工具,如 https://regex101.com/r/vICSfO/1

    为了让我们能够帮助您,我建议您询问业务逻辑,一些实际案例。 例如,您的正则表达式在我看来非常复杂,但也许出于某种原因您需要它。 乍一看,它可以简化:

    ^(?![aeiou]{3,})[a-zA-Z]{2,}$
    

    至少,你需要更换 {3,} 通过 {2,} 如果您需要匹配2个字符的单词。

        3
  •  0
  •   mickmackusa    10 月前

    这个 {3,} 在非捕获组中,要求字符串长度至少为3个字符。如果你想允许 Li ,减少到 {2,} .

    否定展望中的否定字符类( (?![^aeiou]{4,} )至少有4个辅音,所以 ntch 满足该条件并取消输入字符串的资格。如果您想允许 Drantch ,增加到 (?![^aeiou]{5,} .

    密码 Demo )

    $array = [
        'Nartinez',
        'Drantch',
        'Dratch',
        'Xtmnprwq',
        'Yelendez',
        'Boldberg',
        'Yelenovich',
        'Allash',
        'Mohamed',
        'Li',
    ];
    
    $regex = <<<REGEX
    /
    ^
    (?![aeiou]{3,})
    (?:
       \D(?![^aeiou]{5,}[aeiou]*)
       (?![aeiou]{4,})
    ){2,}
    $
    /ix
    REGEX;
    
    var_export(preg_grep($regex, $array));
    

    输出

    array (
      0 => 'Nartinez',
      1 => 'Drantch',
      2 => 'Dratch',
      4 => 'Yelendez',
      5 => 'Boldberg',
      6 => 'Yelenovich',
      7 => 'Allash',
      8 => 'Mohamed',
      9 => 'Li',
    )
    

    至于提高模式的可读性,最好表达你的确切意图,然后在“核心”要求所有字符必须是字母并具有最小字符长度之前生成一组否定的lookahead。

    $regex = <<<REGEX
    /
    ^
    (?![aeiou]{3})    #doesn't start with 3 consecutive vowels
    (?!.*[aeiou]{4})  #doesn't contain 4 consecutive vowels
    (?!.*[^aeiou]{5}) #doesn't contain 5 consecutive consonants
    [a-z]{2,}         #contains only letters, minimum of 2 characters
    $
    /ix
    REGEX;