RegEx验证逗号分隔的选项列表

时间:2014-05-13 02:05:06

标签: javascript php regex pcre

我使用PHP的过滤功能(具体为FILTER_VALIDATE_REGEXP)来验证输入数据。我有一个选项列表,$ input变量可以从列表中指定一些选项。

选项是(不区分大小写):

  1. 所有
  2. 奖励
  3. 加入
  4. 优惠
  5. verified_checkin
  6. $ input变量几乎可以包含值的任意组合。可能的成功案例包括:

    • 所有(值可以是all 以逗号分隔的其他值列表,但不能同时包含这两个值
    • 奖励,流式传输,加入(以逗号分隔的值列表不包括 all
    • 加入(单个值)

    我能够提出的正则表达式是:

    /^(?:all|(?:checkin|verified_checkin|rewards|join|promotions|stream)?(?:,(?:checkin|verified_checkin|rewards|join|promotion|stream))*)$/
    

    到目前为止,它适用于以下示例场景:

    • all (通过)
    • rewards,join,promotion,checkin,verified_checkin (通过)
    • join (通过)

    但是,它允许带有前导逗号的值并重复:

    • ,promotion,checkin,verified_checkin (以逗号开头但在不应该的时候通过

    此外,检查重复项将是一个奖励,但不一定是必需的。

    • rewards,join,promotion,checkin,join,verified_checkin (重复值,但仍然通过但不像主要逗号那样重要)

    我已经在这里工作了几天并尝试了各种实施,这是我能够得到的最接近的。

    关于如何处理领先逗号误报的任何想法?

    更新:编辑问题以更好地解释重复过滤不是真正的要求,只是奖励。

1 个答案:

答案 0 :(得分:5)

有时正则表达式会让事情变得更加复杂。正则表达式非常擅长匹配模式,但是当您引入依赖于匹配模式数量的外部规则时,事情会变得很复杂。

在这种情况下,我只想在逗号上拆分列表,并根据您刚才描述的规则检查结果字符串。

$valid_choices = array('checkin','join','promotions','rewards','stream','verified_checkin');

$input_string;                       // string to match

$tokens = explode(',' $input_string);

$tokens = asort($tokens);            // sort to tokens to make it easy to find duplicates

if($tokens[0] == 'all' && count($tokens) > 1)
    return FALSE;                    // fail (all + other options)

if(!in_array($tokens[0], $valid_choices))
    return FALSE;                    // fail (invalid first choice)

for($i = 1; $i < count($tokens); $i++)
{
    if($tokens[$i] == $tokens[$i-1])
       return FALSE;                 // fail (duplicates)

    if(!in_array($tokens[$i], $valid_choices))
       return FALSE;                 // fail (choice not valid)
}

修改

由于您编辑了您的指定并且指定重复项是可以接受的,但您肯定需要基于正则表达式的解决方案,那么这个应该这样做:

^(all|((checkin|verified_checkin|rewards|join|promotions|stream)(,(checkin|verified_checkin|rewards|join|promotion|stream))*))$

它不会因重复而失败,但它会照顾或引导或尾随逗号,或所有+其他选择组合。

使用正则表达式过滤掉重复数据将非常困难,但也许并非不可能(如果您使用捕获组占位符进行预测)

第二次编辑

虽然您提到检测重复的条目并不重要,但我想我会尝试制作一个也可以检查重复条目的模式。

正如您在下面所看到的,它不是很优雅,也不容易扩展,但它确实可以通过使用负面预测的有限选项列表来完成工作。

^(all|(checkin|verified_checkin|rewards|join|promotions|stream)(,(?!\2)(checkin|verified_checkin|rewards|join|promotions|stream))?(,(?!\2)(?!\4)(checkin|verified_checkin|rewards|join|promotions|stream))?(,(?!\2)(?!\4)(?!\6)(checkin|verified_checkin|rewards|join|promotions|stream))?(,(?!\2)(?!\4)(?!\6)(?!\8)(checkin|verified_checkin|rewards|join|promotions|stream))?(,(?!\2)(?!\4)(?!\6)(?!\8)(?!\10)(checkin|verified_checkin|rewards|join|promotions|stream))?)$

由于最终的正则表达式是如此之长,我将把它分解为部分,以便更容易理解这个概念:

^(all|
  (checkin|verified_checkin|rewards|join|promotions|stream)
  (,(?!\2)(checkin|verified_checkin|rewards|join|promotions|stream))?
  (,(?!\2)(?!\4)(checkin|verified_checkin|rewards|join|promotions|stream))?
  (,(?!\2)(?!\4)(?!\6)(checkin|verified_checkin|rewards|join|promotions|stream))?
  (,(?!\2)(?!\4)(?!\6)(?!\8)(checkin|verified_checkin|rewards|join|promotions|stream))?
  (,(?!\2)(?!\4)(?!\6)(?!\8)(?!\10)(checkin|verified_checkin|rewards|join|promotions|stream))?
 )$/

你可以看到形成模式的机制有点迭代,如果你想提供一个不同的列表,但是这样的模式可以由算法自动生成,但结果模式会变得相当大,而且很快。