Подтвердить что ты не робот

Strpos() с несколькими иглами?

Я ищу функцию типа strpos() с двумя существенными отличиями:

  • Уметь принимать несколько игл. Я имею в виду тысячи игл на них.
  • Искать все вхождения игл в стоге сена и возвращать массив начальных позиций.

Конечно, это должно быть эффективным решением, а не только циклом каждой иглы. Я просматривал этот форум, и у него были похожие вопросы, например:

но нижняя из них была тем, что я ищу. Я использую strpos, чтобы лучше проиллюстрировать мой вопрос, возможно, для этой цели нужно использовать что-то совершенно другое.

Я знаю Zend_Search_Lucene, и мне интересно, можно ли его использовать для достижения этого и как (просто общая идея)?

Большое спасибо за вашу помощь и время!

4b9b3361

Ответ 1

Вот пример кода для моей стратегии:

function strpos_array($haystack, $needles, $offset=0) {
    $matches = array();

    //Avoid the obvious: when haystack or needles are empty, return no matches
    if(empty($needles) || empty($haystack)) {
        return $matches;
    }

    $haystack = (string)$haystack; //Pre-cast non-string haystacks
    $haylen = strlen($haystack);

    //Allow negative (from end of haystack) offsets
    if($offset < 0) {
        $offset += $heylen;
    }

    //Use strpos if there is no array or only one needle
    if(!is_array($needles)) {
        $needles = array($needles);
    }

    $needles = array_unique($needles); //Not necessary if you are sure all needles are unique

    //Precalculate needle lengths to save time
    foreach($needles as &$origNeedle) {
        $origNeedle = array((string)$origNeedle, strlen($origNeedle));
    }

    //Find matches
    for(; $offset < $haylen; $offset++) {
        foreach($needles as $needle) {
            list($needle, $length) = $needle;
            if($needle == substr($haystack, $offset, $length)) {
                $matches[] = $offset;
                break;
            }
        }
    }

    return($matches);
}

Я реализовал простой метод грубой силы выше, который будет работать с любой комбинацией игл и стогов сена (а не только слов). Для возможно более быстрых алгоритмов проверьте:


Другое решение

function strpos_array($haystack, $needles, $theOffset=0) {
    $matches = array();

    if(empty($haystack) || empty($needles)) {
        return $matches;
    }

    $haylen = strlen($haystack);

    if($theOffset < 0) {  // Support negative offsets
        $theOffest += $haylen;
    }

    foreach($needles as $needle) {
        $needlelen = strlen($needle);
        $offset = $theOffset;

        while(($match = strpos($haystack, $needle, $offset)) !== false) {
            $matches[] = $match;
            $offset = $match + $needlelen;
            if($offset >= $haylen) {
                break;
            }
        }
    }

    return $matches;
}

Ответ 2

Я знаю, что это не отвечает на вопрос OP, но хочет комментировать, так как эта страница находится на вершине Google для strpos с несколькими иглами. Здесь простое решение сделать это (опять же, это не относится к вопросу OP - извините):

    $img_formats = array('.jpg','.png');
    $missing = array();
    foreach ( $img_formats as $format )
        if ( stripos($post['timer_background_image'], $format) === false ) $missing[] = $format;
    if (count($missing) == 2)
        return array("save_data"=>$post,"error"=>array("message"=>"The background image must be in a .jpg or .png format.","field"=>"timer_background_image"));

Если в массив $missing добавлено 2 элемента, это означает, что вход не удовлетворяет ни одному из форматов изображений в массиве $img_formats. В этот момент вы знаете, что можете вернуть ошибку и т.д. Это легко можно превратить в небольшую функцию:

    function m_stripos( $haystack = null, $needles = array() ){
        //return early if missing arguments 
        if ( !$needles || !$haystack ) return false; 
        // create an array to evaluate at the end
        $missing = array(); 
        //Loop through needles array, and add to $missing array if not satisfied
        foreach ( $needles as $needle )
            if ( stripos($haystack, $needle) === false ) $missing[] = $needle;
        //If the count of $missing and $needles is equal, we know there were no matches, return false..
        if (count($missing) == count($needles)) return false; 
        //If we're here, be happy, return true...
        return true;
    }

Вернемся к нашему первому примеру, используя вместо этого функцию:

    $needles = array('.jpg','.png');
    if ( !m_strpos( $post['timer_background_image'], $needles ) )
        return array("save_data"=>$post,"error"=>array("message"=>"The background image must be in a .jpg or .png format.","field"=>"timer_background_image"));

Конечно, то, что вы делаете после того, как функция возвращает true или false, зависит от вас.

Ответ 4

Кажется, вы ищете целые слова. В этом случае это может помочь. Поскольку он использует встроенные функции, он должен быть быстрее, чем пользовательский код, но вы должны его профиль:

$words = str_word_count($str, 2);

$word_position_map = array();

foreach($words as $position => $word) {
    if(!isset($word_position_map[$word])) {
        $word_position_map[$word] = array();
    }
    $word_position_map[$word][] = $position;
}

// assuming $needles is an array of words
$result = array_intersect_key($word_position_map, array_flip($needles));

Сохранение информации (например, игл) в нужном формате улучшит время выполнения (например, поскольку вам не нужно вызывать array_flip).

Примечание из str_word_count:

Для этой функции слово "слово" определяется как строка, зависящая от языка, содержащая алфавитные символы, которые также могут содержать, но не начинаться с символов "и" -".

Итак, убедитесь, что вы правильно установили локаль.

Ответ 5

Вы можете использовать регулярное выражение, они поддерживают операции ИЛИ. Это, однако, сделало бы его довольно медленным, по сравнению с strpos.