当前位置: 首页 > 知识库问答 >
问题:

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

壤驷华辉
2023-03-14

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

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

  1. 所有
  2. 奖励
  3. 加入
  4. 促销
  5. 签入
  6. verified_checkin

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

  • all(值可以是all,也可以是逗号分隔的其他值列表,但不能同时是两者)

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

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

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

  • 全部(通过)

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

  • 升级、签入、已验证的签入(以逗号开头,但在不应该时也会传递)

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

  • <代码>奖励、加入、晋升、签入、加入、已验证的签入(重复值,但仍然通过,但没有前导逗号那么重要)

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

关于如何处理前面的逗号误报,有什么想法吗?

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

共有1个答案

洪育
2023-03-14

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

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

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

您可以看到,形成模式的机制在某种程度上是迭代的,如果您想提供不同的列表,则可以通过算法自动生成这样的模式,但生成的模式会变得相当大,速度也相当快。

 类似资料:
  • 是有效输入。 无效 有效 无效 无效(只有7个数字) 我尝试了,但它接受重复的数字

  • 使用JavaScript,我只需要接受数字和逗号。 我使用的regex模式如下所示 还有一些类似的问题只是部分地处理了这一点: 带有逗号分隔符的数字的regex验证(只有整数,没有小数部分) 小数正则表达式,其中小数后面的数字是可选的(没有逗号分隔,小数部分限制为1位数) JavaScript函数需要允许数字、点和逗号(点、逗号和数字以任意顺序匹配)

  • 我试图验证数字1-8的逗号分隔列表。

  • 我如何修改我的regex来为那些以“”或“,”开头,以“”或“,”结尾的字符串返回false

  • 问题内容: 我需要运行类似的查询: 但是我希望子选择返回逗号分隔的列表,而不是数据列。这有可能吗?如果可以,怎么办? 问题答案: 您可以使用GROUP_CONCAT执行该操作,例如