Что более эффективно между str_pos и preg_match?

После этого вопроса: Шаблон для проверки единичного вхождения в preg_match_all

Я понимаю, что мой шаблон должен содержать только одно слово за цикл, потому что в случае, описанном в этом вопросе, я должен найти «Microsoft» и «Microsoft Exchange», и я не могу изменить свое регулярное выражение, потому что эти две возможности задаются динамически из база данных!

Итак, мой вопрос: что является лучшим решением между более чем 200 preg_match и теми же номерами str_pos, чтобы проверить, содержит ли подмножество char эти слова?

Я пытаюсь написать возможный код для обоих решений:

$array= array(200+ values);
foreach ($array as $word)
{
    $pattern='<\b(?:'.$word.')\b>i';
    preg_match_all($pattern, $text, $matches);
    $fields['skill'][] = $matches[0][0];
}

альтернатива:

$array= array(200+ values);
foreach ($array as $word)
{
    if(str_pos($word, $text)>-1)
    {
    fields['skill'][] = $word;
    }
}

person Filippo1980    schedule 16.03.2017    source источник
comment
Функции на основе REGEX работают медленнее, чем большинство других строковых функций. Кстати, ваш тест также может сделать это с одним регулярным выражением, если вы сделаете это как $pattern='<\b(?:'.$word1.'|'.$word2.'|'.$word3.'|'.$word4.')\b>i';, и сколько слов вы можете использовать одновременно, зависит от того, насколько длинным может быть регулярное выражение. Я создал тестовое регулярное выражение длиной 12004 символа. Вроде не макс.   -  person JustOnUnderMillions    schedule 16.03.2017
comment
str_pos() обычно в 3-20 раз быстрее, чем preg_match, потому что preg_match в основном используется для проверки формата строки и извлечения ее разделов на основе регулярных выражений.   -  person Ross Keddy    schedule 16.03.2017


Ответы (2)


strpos намного быстрее, чем preg_match, вот тест:

$array = array();
for($i=0; $i<1000; $i++) $array[] = $i;
$nbloop = 10000;
$text = <<<EOD
I understand that my pattern must contain only a word per cycle because, in the case reported in that question, I must find "microsoft" and "microsoft exchange" and I can't modify my regexp because these two possibilities are given dinamically from a database!

So my question is: which is the better solution between over 200 preg_match and the same numbers of str_pos to check if a subset of char contains these words?
EOD;

$start = microtime(true);
for ($i=0; $i<$nbloop; $i++) {
    foreach ($array as $word) {
        $pattern='<\b(?:'.$word.')\b>i';
        if (preg_match_all($pattern, $text, $matches)) {
            $fields['skill'][] = $matches[0][0];
        }
    }
}
echo "Elapse regex: ", microtime(true)-$start,"\n";


$start = microtime(true);
for ($i=0; $i<$nbloop; $i++) {
    foreach ($array as $word) {
        if(strpos($word, $text)>-1) {
            $fields['skill'][] = $word;
        }
    }
}
echo "Elapse strpos: ", microtime(true)-$start,"\n";

Вывод:

Elapse regex: 7.9924139976501
Elapse strpos: 0.62015008926392

Это примерно в 13 раз быстрее.

person Toto    schedule 16.03.2017

Функции на основе REGEX работают медленнее, чем большинство других строковых функций.

Кстати, ваш тест также может сделать это с одним регулярным выражением, если вы сделаете это как $pattern='<\b(?:'.$word1.'|'.$word2.'|'.$word3.'|'.$word4.')‌​\b>i';, и сколько слов вы можете использовать одновременно, зависит от того, насколько длинным может быть регулярное выражение. Я создал тестовое регулярное выражение длиной 12004 символа. Вроде не макс.

Версия регулярного выражения (один вызов):

$array= array(200+ values);

$pattern='<\b(?:'.implode('|',$array).')\b>i';
preg_match_all($pattern, $text, $matches);
//$fields['skill'][] = $matches[0][0]; 

версия strpos (множественные вызовы)

$array= array(200+ values);
foreach ($array as $word){
 if(strpos($word, $text)!==false)//not with >-1 wont work.
 {
   fields['skill'][] = $word;
 }
}

Если вы ищете отдельные слова, strpos будет соответствовать Hello в HelloWorld, поэтому, если вам нужны только настоящие разделенные слова, вы можете сделать:

$arrayOfWords = explode(' ',$string);
//and now you can check array aginst array 
$array= array(200+ values);
foreach ($array as $word){
 if(in_array($word,$arrayOfWords))//not with >-1 wont work.
 {
   fields['skill'][] = $word;
 }
}
//you can makes this also faster if you array_flip the arrayOfWords 
//and then check with 'isset' (more faster than 'in_array')

То, что вы также хотите сопоставить словосочетания («обмен Microsoft»), не может быть сделано таким образом, если у вас нет этих сочетаний в вашем списке слов.

*добавлены комментарии

person JustOnUnderMillions    schedule 16.03.2017
comment
Спасибо за ваш ответ, однако у вашего регулярного выражения есть проблема ... как я уже сказал, если я ищу обмен Microsoft и Microsoft по одной и той же фразе, ваше решение найдет только один результат! - person Filippo1980; 17.03.2017
comment
@ Filippo1980 Хорошо, но проверенный ответ также не получит обмен Microsoft, только если вы ищете обмен Microsoft, а не только Microsoft, и мой ответ больше указывал на что быстрее. И мое регулярное выражение такое же, как ваше регулярное выражение, только с поиском более одного слова одновременно ;-) И ваш вопрос был действительно о предварительном выполнении, а не о результатах, которые вы хотите. --› Итак, мой вопрос:... - person JustOnUnderMillions; 17.03.2017