php 中的近似字符串匹配

Approximate string matching in php

假设我需要比较两个变量

$team="Benfica(U23):Vitoria Guimaraes(U23)";

$team2="Benfica U23:Vitoria Guimaraes U23";

为了我的目的,$team 和 $team2 应该匹配,实际上它们是同一个灯具,只是将 U23 更改为 (U23)。

然而 preg_match 不像人类那么聪明,所以如果我使用

if (preg_match("/$team/",$team2)) {
echo "they match";
}

当然,它们不匹配。我怎样才能做一个近似值 preg_match 这样 $team 和 $team2 应该被 preg_match 检查认为是相同的(非常相似)? 例如,在上面的 preg_match 中,由于 $team 中的括号 () 而导致 4 个字符错误,我可以允许这些错误通过 preg_match 吗?

可能还有其他情况,例如 $team2 可能...

$team2="U23 Benfica:Vitoria Guimaraes";
$team2="Benfica (U23):Vitoria Guimaraes U23";
$team2="Benfica U23:Vitoria Guimaraes(U23)";

等等..各种情况,这只是一个例子。所有这些都应该与 $team 匹配,我该怎么做才能在 php 中执行这个近似字符串匹配?

谢谢

编辑:我在看到关于使用 similar_text 的评论后删除了这个答案,但我在测试字符串上的实验只给出了 team1 和 team2 之间的 78%。这可能已经足够好了,但我认为替代方案可能会有用。

值得注意的是,除非你开始使用各种伪人工智能的东西,否则你永远不会得到像人类识别这样的东西。从表面上看,您 'sure' 每个单词至少拼写相同。我建议将字符串按任何非字母数字字符拆分为一个数组,然后对数组进行排序,并检查它是否与每个团队的 'master' 匹配。

它看起来像(这是未经测试的 - $pattern 可能需要更多工作)

<?php
  $teamString = "Benfica (U23):Vitoria Guimaraes U23";
  $masterArray = ['Benfica','Guimares','U23','Vitoria'];
  $pattern = '/[^a-zA-Z\d]/';
  $teamArray = preg_split( $pattern, $teamString );
  sort($teamArray);
  $interArray = array_unique($teamArray); //to remove duplicate U23
  $finalArray = array_filter($interArray, function($k) {
    return (preg_replace('/\s+/', '', $k) != ''); //to get rid of whitespace
  });
  //...compare $finalArray with $masterArray

当然你可以添加逻辑来解决你发现的其他问题...

你可以用levenshtein($team, $team2)有一个数字来表示字符串有多少不同,然后定义一个阈值来决定你想要容忍多少。

if (levenshtein($team, $team2) < 3) {
    echo "string are similar";
} else {
    echo "string are not similar";
}

http://php.net/manual/en/function.levenshtein.php

另一种方法是在多重分解数组上使用array_intersect。

删除 () 并替换为 space。
在 space 和冒号上展开并过滤掉所有空的。

使用 array_intersect 查看有多少个相同的项目,看看是否与唯一项目的数量相匹配。

如果您愿意,这当然可以通过计数是否在一定范围内进行校准。

$team1="U23 Benfica:Vitoria Guimaraes";
$team2="Benfica (U23):Vitoria Guimaraes U23";
var_dump(match($team1, $team2));

$team1="U23 Benfica:Vitoria Guimaraes";
$team2="Benfica U23:Vitoria Guimaraes(U23)";
var_dump(match($team1, $team2));

$team1="Benfica U23:Vitoria Guimaraes(U23)";
$team2="Benfica (U23):Vitoria Guimaraes U23";
var_dump(match($team1, $team2));

function match($s1, $s2){
    // remove the ( and ) and replace with space
    $s1 = str_replace(["(",")"], " ", $s1); 
    $s2 = str_replace(["(",")"], " ", $s2);

    $delimiters = [" ", ":"]; // add more delimiters if needed
    // explode on $delimiters and remove empty values
    $arr1 = array_filter(multiexplode($delimiters,$s1)); 
    $arr2 = array_filter(multiexplode($delimiters,$s2));
//var_dump($arr1, $arr2);

    // How many items is equal between $arr1 and $arr2
    $intersect = count(array_unique(array_intersect($arr1, $arr2)));

    // is the count of equal items the same as the count of items in the strings
    if($intersect == count(array_unique($arr1)) && $intersect == count(array_unique($arr2))){
        return true;
    }else{
        return false;
    }

}

// From PHP manual explode
function multiexplode ($delimiters,$string) {

    $ready = str_replace($delimiters, $delimiters[0], $string);
    $launch = explode($delimiters[0], $ready);
    return  $launch;
}

Returns:

bool(true)
bool(true)
bool(true)

https://3v4l.org/MY7j7