搜索与组的顺序无关的字符串的组

时间:2019-06-24 08:57:59

标签: php regex

我有一些字符串组,我需要通过正则表达式查找所有组,而组的顺序无关紧要

请,我需要在用户的答案中找到所有必需的成分。用户可以按任何顺序放置配料,并且可以用任何字符或字符串(空格,逗号)定界,也不需要定界符。

$string = "banana, strawberry, cherry and chocolate";
$regex = "/(banana)*(strawberry)*(cherry)*(chocolate)/";
if (preg_match($regex, $string)) {
 // do something
}

我的代码中的问题是,如果用户的答案是“草莓,香蕉,樱桃”,则preg_match会将其验证为true,这很糟糕,因为在回答时也需要巧克力。或者,如果我键入“ strwberry”(草莓)而不是草莓,那也是如此。用户的答案必须以任何顺序包括所有4种成分,并且成分名称中不能包含错别字。非常感谢您的提示。

1 个答案:

答案 0 :(得分:1)

关于您的请求:

  

用户可以按任何顺序放置配料,并且可以用任何字符或字符串(空格,逗号)定界,也不需要定界符。

配料顺序没有问题,我们稍后会看到。但是没有定界符是一个非常糟糕的主意!考虑以下示例(水果沙拉):

$ingredients = ['melon', 'orange', 'grape', 'apple'];
$userAnswer = 'watermelonorangegrapeapple';

问题很明显,没有办法用这种会导致误报的约束来区分“西瓜”和“西瓜”。

请不要忘记,用户应对自己写的内容负责,并且在未获得所需结果时会从自己的错误中学习。另一种方法是强制用户使用输入字段一个一个地输入配料。


  

用户的答案必须以任何顺序包括所有4种成分,并且其成分名称中不能包含错别字。

为什么不这样做,但是这次我的观点过于狭窄:如果用户写“ strawberries”而不是“ strawberry”怎么办?这实际上不是错字,我认为可以接受。


可能性:

让我们假设在所有可能的世界中,最好的东西都是最好的:单词是定界的,没有错字。

按照previously linked question中的建议,您可以执行以下操作:

if ( preg_match('~(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b)(?=.*\bword4\b)~Ai', $userAnswer) ) {
    //...
}

但这并不是紧凑的,正确的梦想方式:

  1. 它不考虑帐户定界符。
  2. 您必须为每个成分列表动态构建模式。 (但是并不困难)
  3. 每个前瞻都必须遍历整个字符串。
  4. 它既不灵活也不可扩展。
  5. 如果您对第2点到第5点有疑问,请参阅第1点。

其他方法:您可以使用定界符分割用户字符串,然后使用array_diff查看是否存在每种成分。

基本:

$delimiter = '~ \b \s* (?: , \s* | \s and \s+ ) ~uxi';

$parts = preg_split($delimiter, $userAnswer, -1, PREG_SPLIT_NO_EMPTY);

if ( empty(array_diff($ingredients, $parts)) ) {
    // all ingredients are here
}

经过消毒:

$delimiter = '~ \b (?: [ ]? , [ ]? | [ ] and [ ] ) ~ux';

$userAnswer = trim(preg_replace('~[\s\pP]+~u', ' ', mb_strtolower($userAnswer)));

$parts = preg_split($delimiter, $userAnswer);

if ( empty(array_diff($ingredients, $parts)) ) {
    // all ingredients are here
}

在字符串之间比较宽大:

$delimiter = '~ \b (?: [ ]? , [ ]? | [ ] and [ ] ) ~ux';

$userAnswer = trim(preg_replace('~[\s\pP]+~', ' ', mb_strtolower($userAnswer)));

$parts = preg_split($delimiter, $userAnswer);

if ( empty(array_udiff($ingredients, $parts, $callback)) ) {
    // all ingredients are here
}

回调函数示例:

array_udiff的回调函数只不过是用于对数组进行排序的比较函数,换句话说,排序是在后台进行两个数组比较的必要步骤。这就是为什么两个项目之间的比较应该得出正,负整数或0来确定顺序的原因。

PHP具有两个用于在字符串之间执行模糊比较的功能:similar_text()levenshtein()

使用左手边距离的示例。少于2表示只能替换,插入或删除一个字符,以使两个字符串相等(有关更多控制,请参见PHP手册)。

$callback = function ($a, $b) {
    return levenshtein($a, $b) < 2 ? 0 
                                   : ( $a < $b ? -1 : 1 ); 
}

请注意,由于similar_text()为O(max(m,n)^ 3)且levenshtein()为O(m * n)(m和n是字符串的长度)。如果出现问题,您还可以使用metaphone()soundex()之类的函数对字符串进行转换,然后再进行比较或编写自己的转换。这涉及必须预先修改包含成分的数据结构,以使比较变得容易。