Strpos() с несколькими иглами?
Я ищу функцию типа strpos() с двумя существенными отличиями:
- Уметь принимать несколько игл. Я имею в виду тысячи игл на них.
- Искать все вхождения игл в стоге сена и возвращать массив начальных позиций.
Конечно, это должно быть эффективным решением, а не только циклом каждой иглы. Я просматривал этот форум, и у него были похожие вопросы, например:
но нижняя из них была тем, что я ищу. Я использую strpos, чтобы лучше проиллюстрировать мой вопрос, возможно, для этой цели нужно использовать что-то совершенно другое.
Я знаю Zend_Search_Lucene, и мне интересно, можно ли его использовать для достижения этого и как (просто общая идея)?
Большое спасибо за вашу помощь и время!
Ответы
Ответ 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, зависит от вас.
Ответ 3
попробуйте выполнить сопоставление для нескольких
if (preg_match('/word|word2/i', $str))
Проверка нескольких значений strpos
Ответ 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.