2015-08-31 3 views
1

Я столкнулся с проблемами с php functin для оптимизации строки поиска для запроса mssql.Удалить слова остановки из searchstring в PHP

Мне нужно найти запись, которая выглядит как «хоббит», ища «хоббит». Я думал о разрезании статей (в германии у нас есть «der», «die» и «das»), если у них есть конечное пространство из строки поиска.

моя функция выглядит следующим образом:

 public function optimizeSearchString($searchString) 
     { 
     $articles = [ 
      'der ', 
      'die ', 
      'das ', 
      'the ' 
     ]; 


     foreach ($articles as $article) { 
//only cut $article out of $searchString if its longer than the $article itself 
      if (strlen($searchString) > strlen($article) && strpos($searchString, $article)) { 
      $searchString = str_replace($article, '', $searchString); 
      break; 
      } 
     } 

     return $searchString; 
     } 

, но это не работает ...

Может быть, есть приятное решение, используя регулярные выражения?

+0

Этот тест 'strlen ($ searchString)> strlen ($ article)' абсолютно бесполезен, удалите его. 'strpos' может возвращать 0, которое интерпретируется как false. Вы должны написать 'strpos (...)! == false'. Вместо того, чтобы делать тесты, замените их напрямую. таким образом вы разбираете строку только один раз. –

+2

Преимущество использования 'preg_replace' здесь заключается в том, чтобы избежать ложных срабатываний с использованием границ слов для разграничения слов и для удаления всех из них за один проход с использованием чередования. Шаблон не является сложным, быстрое руководство по регулярному выражению решит проблему. –

+0

Я попытался '$ optimizedString = preg_replace ("/(der \ s | die \ s | das \ s | the \ s)/", '', $ searchString);' но это, похоже, не сработает ... – bambamboole

ответ

2

Решение, предлагаемое @Jonny 5, кажется лучшим для моего решения.

Теперь я использую функцию, как это:

public function optimizeSearchString($searchString = "") 
    { 
    $stopwords = array(
     'der' => 1, 
     'die' => 1, 
     'das' => 1, 
     'the' => 1); 

    $words = preg_split('/[^-\w\']+/', $searchString, -1, PREG_SPLIT_NO_EMPTY); 

    if (count($words) > 1) { 
     $words = array_filter($words, function ($v) use (&$stopwords) { 
     return !isset($stopwords[strtolower($v)]); 
     } 
    ); 
    } 

    if (empty($words)) { 
     return $searchString; 
    } 

    return implode(" ", $words); 
    } 

Новое решение Jonny 5 будет работать также, но я использую этот, потому что я не знаком с регулярным выражением, и я знаю, что происходит :-)

+0

Отлично, что помогло! Я также опубликовал ответ с другим решением:] –

3

1.) Для того, чтобы просто удалить один из стоп-словначала или конца строки с помощью regex like this:

~^\W*(der|die|das|the)\W+\b|\b\W+(?1)\W*$~i 
  • ~ является pattern delimiter
  • ^ каретка anchor начало матчей строка
  • \W (верхний) является short для символа, который не является word character
  • (der|die|das|the) Чередования | в первых скобках group
  • \b соответствует word boundary
  • В (?1) структура первой группы наклеивается
  • $ матчей правильных после последнего символа в строке
  • Подержанный i(PCRE_CASELESS)flag. Если вводится utf-8, также необходимо u(PCRE_UTF8) флаг.

Reference - What does this regex mean

Генерация шаблона:

// array containing stopwords 
$stopwords = array("der", "die", "das", "the"); 

// escape the stopword array and implode with pipe 
$s = '~^\W*('.implode("|", array_map("preg_quote", $stopwords)).')\W+\b|\b\W+(?1)\W*$~i'; 

// replace with emptystring 
$searchString = preg_replace($s, "", $searchString); 

Примечание что если ~ разделителем происходит в $stopwords массиве, он также должен быть экранированы с обратной косой черты.

PHP test at eval.in, Regex pattern at regex101


2.) Но удалить стоп-слова в любом месте строки как насчет разделения на слова:

// words to be removed 
$stopwords = array(
'der' => 1, 
'die' => 1, 
'das' => 1, 
'the' => 1); 
# used words as key for better performance 

// remove stopwords from string 
function strip_stopwords($str = "") 
{ 
    global $stopwords; 

    // 1.) break string into words 
    // [^-\w\'] matches characters, that are not [0-9a-zA-Z_-'] 
    // if input is unicode/utf-8, the u flag is needed: /pattern/u 
    $words = preg_split('/[^-\w\']+/', $str, -1, PREG_SPLIT_NO_EMPTY); 

    // 2.) if we have at least 2 words, remove stopwords 
    if(count($words) > 1) 
    { 
    $words = array_filter($words, function ($w) use (&$stopwords) { 
     return !isset($stopwords[strtolower($w)]); 
     # if utf-8: mb_strtolower($w, "utf-8") 
    }); 
    } 

    // check if not too much was removed such as "the the" would return empty 
    if(!empty($words)) 
    return implode(" ", $words); 
    return $str; 
} 

См demo at eval.in, ideone.com

// test it 
echo strip_stopwords("The Hobbit das foo, der"); 

Хоббит Foo

Это решение также удалите любые знаки препинания, кроме _-', потому что он уничтожает оставшиеся слова с пробелом после удаления общих слов. Идея состоит в том, чтобы подготовить строку для запроса.

Оба решения не изменяют корпус и оставляют строку, если она состоит только из за одно мгновение.

Списки общих слов

+0

Не могли бы вы объяснить, почему вы передаете '$ stopwords' по ссылке в закрытии' array_filter' во втором коде? Я спрашиваю из-за [этого] (http://stackoverflow.com/a/3845530/4946451) сообщения о значении по сравнению с эталонной производительностью. Разве не лучше было бы здесь по стоимости? – arkuuu

1

Это то, что я делаю.

public function optimizeSearchString($searchString) { 
    $wordsFromSearchString = str_word_count($searchString, true); 
    $finalWords = array_diff($wordsFromSearchString, $stopwords); 
    return implode(" ", $finalWords); 
} 
Смежные вопросы