【问题标题】:RegEx to validate a comma separated list of options正则表达式验证逗号分隔的选项列表
【发布时间】:2014-06-30 13:32:55
【问题描述】:

我正在使用 PHP 的过滤器函数(特别是 FILTER_VALIDATE_REGEXP)来验证输入数据。我有一个选项列表,$input 变量可以从列表中指定多个选项。

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

  1. 全部
  2. 奖励
  3. 加入
  4. 促销活动
  5. 签到
  6. verified_checkin

$input 变量几乎可以有任何值的组合。可能的成功案例有:

  • 所有(值可以是all 其他值的逗号分隔列表,但不能同时使用两者)
  • rewards,stream,join (逗号分隔的值列表排除 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 (重复值但仍然通过但不如前导逗号重要)

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

关于如何处理前导逗号误报的任何想法?

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

【问题讨论】:

  • 您的正则表达式实际上并没有用逗号分隔列表。
  • @VasiliSyrakis List 我说过,预期的输入可以是逗号分隔的值。它们 RegEx 不必真正将它们分开。它只需要验证所有值是否都来自提供的列表:)
  • @UzairSajid - 查看我编辑的答案

标签: javascript php regex pcre


【解决方案1】:

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

在这种情况下,我只需用逗号拆分列表,并根据您刚刚描述的规则检查生成的字符串。

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

您可以看到形成模式的机制有些迭代,如果您想提供不同的列表,算法可以自动生成这样的模式,但生成的模式会变得相当大,相当快。

【讨论】:

  • +1 @Miky 只是在 cmets 中提出同样的建议,并在我打字时看到您发布了答案。
  • 尽管我很想使用像你描述的更简单的解决方案,但我不能,因为这个验证逻辑是一个更大的验证库的一部分,其中定义了验证逻辑一组过滤器和一个非常通用的处理器来解析它们。
  • 谢谢,在这里添加了一些 php 构建代码,很容易移植到其他语言:stackoverflow.com/q/34433034/551357
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-26
  • 1970-01-01
相关资源
最近更新 更多