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


RegEx to validate a comma separated list of options

我使用PHP的Filter Functions (FILTER_VALIDATE_REGEXP)来验证输入数据。我有一个选项列表,$input变量可以从列表中指定一些选项。

选项如下(不区分大小写):

  1. <
  2. 奖励/gh>
  3. 促销
  4. 签入
  5. verified_checkin

$input变量几乎可以是这些值的任意组合。成功的可能情况有:

  • all (值可以是all 其他值的逗号分隔列表,但不能同时是)
  • rewards,stream,join (逗号分隔的值列表,不包括 all)
  • join (单个值)

我能想到的正则表达式是:

/^(?:all|(?:checkin|verified_checkin|rewards|join|promotions|stream)?(?:,(?:checkin|verified_checkin|rewards|join|promotion|stream))*)$/
目前为止,它适用于以下示例场景:

  • all (passed)
  • rewards,join,promotion,checkin,verified_checkin (passed)
  • join (passed)

但是,它允许一个以逗号开头的值,并通过:

  • ,promotion,checkin,verified_checkin (以逗号开头,但在不需要的时候也通过)

另外,检查重复项也是额外的,但不一定是必需的。

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

我已经做了几天了,尝试了各种各样的实现,这是我能得到的最接近的。

对于如何处理前导逗号误报有什么想法吗?

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

有时候正则表达式只是让事情变得比它们应该的更复杂。正则表达式非常擅长匹配模式,但是当你引入依赖于匹配模式数量的外部规则时,事情会很快变得复杂。

在本例中,我将用逗号分隔列表,并根据您刚才描述的规则检查结果字符串。

$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))?
 )$/

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