【问题标题】:PHP Search Array With Multiple Keywords And Sorted Result具有多个关键字和排序结果的 PHP 搜索数组
【发布时间】:2018-03-04 12:13:01
【问题描述】:

我有一个计划,从我准备的txt文件中进行搜索,txt文件内容类似下面这样

一个.txt

Amy Jefferson
Nathalie Johnson
Emma West
Donna Jefferson
Tanya Nathalie
George West
Emma Watson
Emma Jefferson

如果代码是这样的

一个.php

$filename = "a.txt";
$example = file($filename, FILE_IGNORE_NEW_LINES);
$searchword = 'Emma Jefferson';
$matches = array();
foreach($example as $k=>$v) {
    if(preg_match("/\b$searchword\b/i", $v)) {
        $matches[$k] = $v;
        echo $matches[$k]."<br>";
    }
}

结果只会是“Emma Jefferson”

如果我使用这个代码

b.php

$filename = "a.txt";
$example = file($filename, FILE_IGNORE_NEW_LINES);
$searchword = 'Emma Jefferson';
$matches = array();
foreach($example as $k=>$v) {
    $searchword2 = str_ireplace(" ", "|", $searchword);
    if(preg_match("/\b$searchword2\b/i", $v)) {
        $matches[$k] = $v;
        echo $matches[$k]."<br>";
    }
}

结果会是这样的

Amy Jefferson
Emma West
Donna Jefferson
Emma Watson
Emma Jefferson

唯一的结果,但最后一个结果是“Emma Jefferson”

所以问题是我如何搜索 Emma Jefferson,结果排序是这样的

Emma Jefferson
Emma Watson
Emma West
Amy Jefferson
Donna Jefferson

所以基本上它首先搜索“Emma Jefferson”整个单词,然后是“Emma”,最后一个是“Jefferson”

更新 我为这个问题投票支持 Don't Panic 代码,但我想在这里感谢所有贡献者 Don't Panic、RomanPerekhrest、Sui Dream、Jere、i-man,你们都是最棒的!

帕蒂极客

【问题讨论】:

    标签: php


    【解决方案1】:

    您当前会立即回显结果,因此它们按文本顺序排列。

    您可以搜索完整字符串和部分匹配,然后搜索concatenate results

    foreach($example as $k=>$v) {
        if(preg_match("/\b$searchword\b/i", $v)) {
            $fullMatches[] = $v;
        }
        if(preg_match("/\b$searchword2\b/i", $v)) {
            $matches[] = $v;
        }
    }
    $matches = array_unique(array_merge($fullMatches, $matches));
    foreach($matches as $k => $v)
        echo $v . "<br>";
    

    更新:

    多词变体:

    $words = ['Emma', 'Jefferson'];
    $matches = array();
    foreach($example as $k => $v) {
        $fullStr = implode(' ', $words);
        if(preg_match("/\b$fullStr\b/i", $v))
            $matches[0][] = $v;
        $str = "";
        $i = 1;
        foreach($words as $word) {
            if ($str === "")
                $str = $word;
            else
                $str .= '|' . $word;
            if(preg_match("/\b$str\b/i", $v))
                $matches[$i][] = $v;
            $i++;
        }
    }
    $result = array();
    foreach($matches as $firstKey => $arr) {
        foreach($arr as $secondKey => $v) {
            $result[] = $v;
        }
    }
    $result = array_unique($result);
    foreach($result as $k => $v)
        echo $v . "<br>";
    

    【讨论】:

    • 感谢 Sui 的回复 :) ,它为 Emma Jefferson 在顶部的工作,但其余的仍然不是我想要的,你给我的代码的结果就像这个 Emma Jefferson Amy杰斐逊艾玛韦斯特唐娜杰斐逊艾玛沃特森同时我想要这样的结果艾玛杰斐逊艾玛沃特森艾玛韦斯特艾米杰斐逊唐娜杰斐逊
    • 是否可以使其动态化,我的意思是搜索可以是任何单词,而不仅仅是 2
    • 我很抱歉 Sui,但是为什么当我尝试你的代码时,a.txt 上的所有数据都显示出来了?
    • @pattygeek ,您在 $words 中输入了您的搜索部分,对吗?此变体应按出现在搜索数组中的单词顺序对答案进行排序。如果要考虑字数进行排序,可以将其与 preg_match_all() 变体结合使用。
    • 是的,这是我使用的代码 pastebin.com/aVVq8a7V 仍然显示所有数据 Sui。
    【解决方案2】:

    复杂的解决方案:

    $lines = file('a.txt', FILE_IGNORE_NEW_LINES);
    $name = 'Emma';
    $surname = 'Jefferson';
    $emmas = $jeffersons = [];
    
    foreach ($lines as $l) {
        if (strpos($l, $name) === 0) {
            $emmas[] = $l;
        } elseif ( strrpos($l, $surname) === (strlen($l) - strlen($surname)) ) {
            $jeffersons[] = $l;
        }
    }
    
    usort($emmas, function($a,$b){
        return strcmp(explode(' ', $a)[1], explode(' ', $b)[1]);
    });
    usort($jeffersons, function($a,$b){
        return strcmp($a, $b);
    });
    
    $result = array_merge($emmas, $jeffersons);
    print_r($result);
    

    输出:

    Array
    (
        [0] => Emma Jefferson
        [1] => Emma Watson
        [2] => Emma West
        [3] => Amy Jefferson
        [4] => Donna Jefferson
    )
    

    【讨论】:

    • 嗨 Roman,感谢您的回复 :) 结果就像我想要的一样,但问题是,是否可以使 $name 和 $surname 动态化,因为这里的搜索可以超过 2 个字
    • @pattygeek,如果搜索可以超过 2 个单词 - 您将如何分别按 4 个或更多单词排序?这对你来说将是相当困难的情况。我的解决方案解决了当前的问题
    【解决方案3】:

    您将不得不编写一个新循环或开始对 Array 后缀进行排序,因为 foreach 循环一次采用一个元素名称,测试它是否与您的搜索词匹配,如果匹配,则名称放在末尾你的新数组$matches[]。所以

        if(preg_match("/\b$searchword2\b/i", $v)) {
        $matches[$k] = $v;
        echo $matches[$k]."<br>";
    }
    

    part 不知道 $matches[] 中存在或不存在的名称。

    所以我的建议是:

    $filename = "a.txt";
    $example = file($filename, FILE_IGNORE_NEW_LINES);
    $searchword = 'Emma Jefferson';
    $matches = array();
    
    
    
    $searchword2 = array($searchword, explode(" ", $searchword)[0], explode(" ", $searchword)[1]);
    $isThisNameAlreadyInTheList;
    
    foreach($searchword2 as $actualSearchword) {
    
        foreach($example as $k=>$v) {
    
            $isThisNameAlreadyInTheList = false;
            foreach($matches as $match) {   
                if(preg_match("/\b$match\b/i", $v)) {
                    $isThisNameAlreadyInTheList = true;
                }
            }
    
            if (!$isThisNameAlreadyInTheList) {
                if(preg_match("/\b$actualSearchword\b/i", $v)) {
                    $matches[$k] = $v;
                    echo $matches[$k]."<br>";
                }
            }
        }
    
    }
    

    【讨论】:

    • 感谢 Jere 对此部分的回复 :) $searchword2 = array("Emma Jefferson", "Emma", "Jefferson");它可以变成动态的吗,我试试 $searchword2 = explode("|", $searchword);但仍然混淆“艾玛杰斐逊”的位置
    • 是的,这是我上传代码时犯的一个错误。现在应该是正确的。
    • 很抱歉回复晚了,杰尔它的工作!但是如果结果超过 2 个单词怎么办?可以在不编辑 $searchword2 的情况下进行搜索吗?
    【解决方案4】:

    我不知道使用正则表达式解决方案来考虑匹配位置的方法,但是如果您将搜索字符串和术语转换为单词数组,则可以做到。

    通过这种方法,我们迭代文本项并为搜索词中的每个单词构建一个位置匹配数组,然后按匹配数对结果进行排序,然后是匹配位置。

    $search_words = explode(' ', strtolower($searchword));
    
    foreach ($example as $item) {
        $item_words = explode(' ', strtolower($item));
    
        // look for each word in the search term
        foreach ($search_words as $i => $word) {
            if (in_array($word, $item_words)) {
    
                // add the index of the word in the search term to the result
                // this way, words appearing earlier in the search term get higher priority
                $result[$item][] = $i;
            }
        }
    }
    
    // this will sort alphabetically if the uasort callback returns 0 (equal)
    ksort($result);
    
    // sort by number of matches, then position of matches    
    uasort($result, function($a, $b) {
        return count($b) - count($a) ?: $a <=> $b;
    });
    
    // convert keys to values    
    $result = array_keys($result);
    

    【讨论】:

    • 嗨,不要惊慌,感谢您的回复 :) 但我仍然得到了我想要的那种,这就是我得到的艾玛杰斐逊艾玛沃特森唐娜杰斐逊艾玛韦斯特艾米杰斐逊,同时结果我想要就像我在上面写的列表中一样
    • 啊,原来如此,不只是匹配的数量,还有匹配的顺序?
    • 是的:D 那部分让我有点困惑:D
    • @pattygeek 我在(希望)正确理解问题后重写了答案。
    • @Don'tPanic,你应该提到 &lt;=&gt; 仅在 PHP 7 之后可用
    【解决方案5】:

    我会像这样使用 preg_match_all 解决方案:

    $searchName = "Emma Jefferson";
    $searchTerms = explode(' ', $searchName);
    
    $pattern = "/(\b$searchTerms[0]\b \b$searchTerms[1]\b)|(\b$searchTerms[0]\b \w+)|(\w* \b$searchTerms[1]\b)/i";
    
    $output = [];
    preg_match_all($pattern, implode(' | ', $example), $out);
    
    foreach($out as $k => $o){
        if($k == 0){
            continue;
        }
    
        foreach($o as $item){
            if(!empty($item)){
                $output[] = $item;
            }
        }
    }
    
    print_r($output);
    

    您也可以将文件作为字符串引入并避免内爆部分。

    【讨论】:

    • 嗨 i-man 感谢您的回复 :) 结果就像我想要的一样!但是模式部分,我如何让它变得动态?谢谢
    • 我更新了我的答案,以使搜索词在名称之间的空格上更加动态地分割
    • 抱歉回复晚了,i-man如果搜索超过2个字怎么办?还能用吗?
    猜你喜欢
    • 1970-01-01
    • 2017-05-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 2021-03-13
    • 1970-01-01
    • 2013-03-25
    • 1970-01-01
    相关资源
    最近更新 更多