Как найти похожий текст в большой строке?

У меня есть большая нить ул и игла ндл. Теперь мне нужно найти аналогичный текст ndl из строки str. Например,

ИСТОЧНИК: «Это демонстрационный текст, и я люблю вас за это».

ИГЛА: "Я тебя люблю"

ВЫВОД: «Я люблю тебя»


ИСТОЧНИК: «У меня есть уникальная идея. Она вам нужна?».

ИГЛА: "уникальная идея"

ВЫВОД: «уникальная идея»

Я обнаружил, что могу сделать это, используя меры сходства, такие как косинус или манхэттоновская мера подобия. Однако я думаю, что реализация этих алгоритмов будет сложной. Не могли бы вы предложить мне какой-либо простой или быстрый способ сделать это, возможно, используя любую библиотечную функцию php? ТИА


person user373100    schedule 27.10.2018    source источник


Ответы (3)


Для достижения этой цели нет встроенной функции PHP. Однако возможности PHP ограничены только вашим воображением. Мы не можем на SO предлагать библиотеки для достижения вашей цели, и вам нужно иметь в виду, что такого рода вопросы могут быть помечены как не по теме. Поэтому вместо того, чтобы предлагать некоторые библиотеки, я просто укажу вам направления, которые вам нужно изучить.

Как задумано, ваш вопрос предполагает, что вам не нужны простые функции сопоставления строк, такие как stripos и co, и регулярное выражение не может этого достичь. Например

уникальный и неповторимый

а также

идея и идея

не может соответствовать этим функциям. Итак, вам нужно искать что-то вроде levenshtein function. Но поскольку вам нужны подстроки, а не обязательно вся строка, а также, чтобы упростить работу для levenshtein function и вашего сервера, вам нужно использовать некоторое воображение. пример break и haystack and needle словами, а затем используйте levenshtein, чтобы найти наиболее близкие значения к вашим иглам.

Это один из способов добиться этого. Внимательно прочитайте комментарии, чтобы понять идею, и вы сможете реализовать что-то лучше.

для строк, содержащих только символы ASCII, добиться этого относительно легко. Но для других кодировок вы, вероятно, столкнетесь со многими трудностями. Но простой подход к обработке многобайтовых строк тоже может выглядеть примерно так:

     function to_ascii($text,$encoding="UTF-8") {
      if (is_string($text)) {
        // Includes combinations of characters that present as a single glyph
        $text = preg_replace_callback('/\X/u', __FUNCTION__, $text);
      }
      elseif (is_array($text) && count($text) == 1 && is_string($text[0])) {
        // IGNORE characters that can't be TRANSLITerated to ASCII
        $text = @iconv($encoding, "ASCII//IGNORE//TRANSLIT", $text[0]);
        // The documentation says that iconv() returns false on failure but it returns ''
        if ($text === '' || !is_string($text)) {
          $text = '?';
        }
        elseif (preg_match('/\w/', $text)) {        // If the text contains any letters...
          $text = preg_replace('/\W+/', '', $text); // ...then remove all non-letters
        }
      }
      else {  // $text was not a string
        $text = '';
      }
      return $text;
    }





function find_similar($needle,$str,$keep_needle_order=false){
    if(!is_string($needle)||!is_string($str))
    {
        return false;
    }
    $valid=array();
    //get  encodings  and words from haystack and needle
    setlocale(LC_CTYPE, 'en_GB.UTF8');
    $encoding_s=mb_detect_encoding($str);
    $encoding_n=mb_detect_encoding($needle);

    mb_regex_encoding ($encoding_n);
    $pneed=array_filter(mb_split('\W',$needle));

    mb_regex_encoding ($encoding_s);
    $pstr=array_filter(mb_split('\W',$str));



    foreach($pneed as $k=>$word)//loop trough needle's words
    {
        foreach($pstr as $key=>$w)
        {
            if($encoding_n!==$encoding_s)
            {//if $encodings are not the same make some transliteration
                $tmp_word=($encoding_n!=='ASCII')?to_ascii($word,$encoding_n):$word; 
                $tmp_w=($encoding_s!=='ASCII')?to_ascii($w,$encoding_s):$w;
            }else
            {
                $tmp_word=$word;
                $tmp_w=$w;
            }

            $tmp[$tmp_w]=levenshtein($tmp_w,$tmp_word);//collect levenshtein distances
            $keys[$tmp_w]=array($key,$w);

        }

        $nominees=array_flip(array_keys($tmp,min($tmp)));//get the nominees
        $tmp=10000;
        foreach($nominees as $nominee=>$idx)
        {//test sound like to get more precision
            $idx=levenshtein(metaphone($nominee),metaphone($tmp_word));
            if($idx<$tmp){
                $answer=$nominee;//get the winner

            }
            unset($nominees[$nominee]);
        }
        if(!$keep_needle_order){
            $valid[$keys[$answer][0]]=$keys[$answer][1];//get the right form of the winner
        }
        else{
            $valid[$k]=$keys[$answer][1];
        }
        $tmp=$nominees=array();//clean a little for the next iteration
    }
    if(!$keep_needle_order)
    {
        ksort($valid);
    }

    $valid=array_values($valid);//get only the values
    /*return the array of the closest value to the 
    needle according to this algorithm of course*/
    return $valid;

}


var_dump(find_similar('i knew you love me','finally  i know you loved me and all my pets'));
var_dump(find_similar('I you love','This is a demo text and I love you about this'));
var_dump(find_similar('a unik idia','I have a unique idea. Do you need?'));
var_dump(find_similar("Goebel, Weiss, Goethe, Goethe und Goetz",'Weiß, Goldmann, Göbel, Weiss, Göthe, Goethe und Götz'));
var_dump(find_similar('Ḽơᶉëᶆ ȋṕšᶙṁ ḍỡḽǭᵳ ʂǐť ӓṁệẗ, ĉṓɲṩḙċťᶒțûɾ ấɖḯƥĭṩčįɳġ ḝłįʈ',
'Ḽơᶉëᶆ ȋṕšᶙṁ ḍỡḽǭᵳ ʂǐť ӓṁệẗ, ĉṓɲṩḙċťᶒțûɾ ấɖḯƥĭṩčįɳġ ḝłįʈ, șếᶑ ᶁⱺ ẽḭŭŝḿꝋď ṫĕᶆᶈṓɍ ỉñḉīḑȋᵭṵńť ṷŧ ḹẩḇőꝛế éȶ đꝍꞎôꝛȇ ᵯáꞡᶇā ąⱡîɋṹẵ.'));

и вывод:

    array(5) {
  [0]=>
  string(1) "i"
  [1]=>
  string(4) "know"
  [2]=>
  string(3) "you"
  [3]=>
  string(5) "loved"
  [4]=>
  string(2) "me"
}
array(3) {
  [0]=>
  string(1) "I"
  [1]=>
  string(4) "love"
  [2]=>
  string(3) "you"
}
array(3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(6) "unique"
  [2]=>
  string(4) "idea"
}
array(5) {
  [0]=>
  string(6) "Göbel"
  [1]=>
  string(5) "Weiss"
  [2]=>
  string(6) "Goethe"
  [3]=>
  string(3) "und"
  [4]=>
  string(5) "Götz"
}
array(8) {
  [0]=>
  string(13) "Ḽơᶉëᶆ"
  [1]=>
  string(13) "ȋṕšᶙṁ"
  [2]=>
  string(14) "ḍỡḽǭᵳ"
  [3]=>
  string(6) "ʂǐť"
  [4]=>
  string(11) "ӓṁệẗ"
  [5]=>
  string(26) "ĉṓɲṩḙċťᶒțûɾ"
  [6]=>
  string(23) "ấɖḯƥĭṩčįɳġ"
  [7]=>
  string(9) "ḝłįʈ"
}

если вам нужен вывод в виде строки, вы можете использовать join в результате функции перед ее использованием

Вы можете запустить рабочий код и проверить результат онлайн

Но вы должны иметь в виду, что это не будет работать для всех типов строк и для всех версий PHP.

person Elementary    schedule 28.10.2018
comment
Ваш код отлично работает для английских предложений. Однако мне нужно многобайтовое решение. Я имею в виду поддержку UTF. Пожалуйста, предложите для юникода. - person user373100; 29.10.2018

Попробуйте этот код, чтобы найти строку в строке

$data = "I have a unique idea. Do you need one?";
$find = "a unique idea";
$start = strpos($data, $find);
if($start){     
    $end = $start + strlen($find);
    print_r(substr($data, $start, strlen($find)));
} else {
    echo "not found";
}
person Ajy    schedule 27.10.2018

Это очень простой способ сделать это:

$source = "This is a demo text and I love you about this";
$needle = "I you love";
$words = explode(" " , $source);
$needleWords = explode(" ", $needle);
$results = [];

foreach($needleWords as $key => $needleWord) {

    foreach($words as $keyWords => $word) {

        if(strcasecmp($word, $needleWord) == 0) {
            $results[$keyWords] = $needleWord;
        }
    }
}
uksort($results, function($a , $b) {
    return $a - $b;
});
echo(implode(" " , $results));

Вывод

I love you
person pierDipi    schedule 27.10.2018
comment
Ваш код отлично работает для английских предложений. Однако, как я уже говорил ранее, мне нужно многобайтовое решение. Я имею в виду поддержку UTF. Пожалуйста, предложите для юникода. - person user373100; 29.10.2018
comment
Можете ли вы предоставить мне провальный тест? - person pierDipi; 29.10.2018