PHP: как правильно использовать Strpos() для поиска слова в строке

Если кто-то имеет опыт работы с PHP, то он знает, как найти целые слова в строке и их положение, используя регулярное выражение и preg_match() или preg_match_all. Но если вы ищете более легкое решение, у вас может возникнуть соблазн попробовать strpos(). Возникает вопрос, как можно использовать эту функцию, чтобы она не обнаруживала подстроки, содержащиеся в других словах. Например, как обнаружить «любые», но не те символы, которые встречаются в «компании»?

Рассмотрим строку, подобную следующей:

"Will *any* company do *any* job, (are there any)?"

Как можно применить strpos() для обнаружения каждого появления «любого» в строке? Реальная жизнь часто включает в себя нечто большее, чем просто слова, разделенные пробелами. К сожалению, это предложение не отображалось с неалфавитными символами, когда я первоначально опубликовал.


person slevy1    schedule 13.06.2017    source источник
comment
Именно для этого были разработаны регулярные выражения.   -  person Jared Farrish    schedule 14.06.2017
comment
Этот вопрос не имеет смысла. strpos() будет всегда определять позицию введенной вами строки. Если это не то, что вы хотите, не используйте его.   -  person billynoah    schedule 14.06.2017
comment
Если вы педантичны в использовании strpos и хотите использовать только any как отдельное слово, просто используйте пробел до и после слова и добавьте единицу к результирующему индексу.   -  person fubar    schedule 14.06.2017
comment
Это может показаться ошибочным, но может быть полезным для тех, кому нужно выжать немного больше производительности. Пожалуйста, не понижайте голос из-за соображений YAGNI, это неплохой вопрос.   -  person Jared Farrish    schedule 14.06.2017
comment
@fubar - спасибо за отзыв. Пожалуйста, взгляните теперь на мой вопрос.   -  person slevy1    schedule 14.06.2017
comment
@ Джаред Фарриш, спасибо!   -  person slevy1    schedule 14.06.2017
comment
Я проголосовал против, потому что считаю, что вопрос без ответа. Он спрашивает, как применить strpos(), чтобы сделать то, чего он просто не делает. Никакое количество изменений исходной строки или итераций не изменит этого. Если на самом деле желаемый результат достигается таким образом, вы сразу же возвращаетесь к тому, что он не является функцией strpos() и (вероятно) мог бы повысить производительность, просто используя регулярное выражение. Для меня этот вопрос похож на вопрос: как я могу использовать оператор == для строгого сравнения, потому что == работает лучше, чем ===   -  person billynoah    schedule 14.06.2017
comment
@billynoa, она (это я) спрашивает, как применить strpos(). Мой ответ показывает 2 способа выполнить этот подвиг в тандеме с другими функциями PHP. Вы можете предпочесть использовать регулярное выражение, но для тех, кто не приемлет регулярное выражение, я показал, как можно использовать strpos(). Вам может не понравиться мой вопрос, но понизить его — значит попытаться ограничить варианты (знания) только теми, которые соответствуют вашему одобрению. Кроме того, правда выйдет наружу независимо от того, насколько сильно вы можете минусовать мои мысли. Как оказалось, истина не может быть уничтожена голосованием, если только человек не живет в условиях тирании, где разрешены только санкционированные идеи.   -  person slevy1    schedule 14.06.2017
comment
@ slevy1 - извините, что предположил, что вы он. Мне не нравится ваш вопрос, я просто не вижу смысла. Я предложил свою причину для отрицательного голосования (что мало кто будет беспокоиться) на случай, если появится какой-нибудь убедительный аргумент, чтобы изменить мое мнение. комментарий, который вы только что сделали, на самом деле не приводит меня туда. но удачи и надеюсь, что вы нашли то, что искали в ответах здесь.   -  person billynoah    schedule 14.06.2017
comment
@billynoah - я думаю, дело было в том, как включить функцию, используя strpos(), которая устраняет необходимость в регулярном выражении. Это 100% ответ. И в самом деле было весело, как мысленный эксперимент.   -  person Jared Farrish    schedule 14.06.2017
comment
@JaredFarrish Если целью является производительность, то это не так просто, поскольку регулярное выражение медленное, strpos быстрое. Поскольку этот вопрос является ответвлением от другого вопроса, я просто использую код из предыдущего вопроса. Вот производительность регулярных выражений 3v4l.org/rpP0o/perf#output и вот stros 3v4l.org/IiVYk/perf#output странно то, что если мы упростим регулярное выражение с помощью '/\bany\b/' код работает значительно медленнее 3v4l.org/SXAau/perf#output . Имея это в виду, я думаю, если вы действительно хотите сосредоточиться на производительности, вам нужно смотреть на каждую часть в отдельности.   -  person Andreas    schedule 14.06.2017
comment
@Andreas - Определение тяжелее и легче спорно относительно того, что это может означать. Очевидно, что большинство подумало бы о производительности, но я предпочитаю игнорировать это, поскольку существуют некоторые различия в том, как это будет реализовано. YMMV.   -  person Jared Farrish    schedule 14.06.2017
comment
@Джаред Фарриш: тяжелее == медленнее и легче == быстрее. И согласно Руководству: Совет Не используйте preg_match(), если вы хотите только проверить, содержится ли одна строка в другой строке. Вместо этого используйте strpos(), так как это будет быстрее. Регулярное выражение наиболее полезно, если вы используете шаблон для обнаружения неизвестного, такого как доменное имя — см. php.net/ прег_матч. Хотя кажется проще просто использовать регулярное выражение с функцией preg_, для оптимальной производительности использование strpos, даже если это может потребовать написания большего количества кода, может быть более эффективным.   -  person slevy1    schedule 14.06.2017
comment
Я не вникаю в исполнение того или иного. Я знаю, что регулярные выражения медленные. Я также использую их все время для самых разных вещей (простых), и ни разу у меня не было проблем с производительностью. YMMV.   -  person Jared Farrish    schedule 15.06.2017
comment
Что бы это ни стоило, чтобы удовлетворить собственное любопытство, я поставил принятый ответ @ slevy1 рядом с Джаредом Фарришем и старым добрым preg_match_all(), чтобы посмотреть, как они выступили. сравнение, по общему признанию, немного несправедливо, поскольку функция Джареда возвращает только первую позицию, в то время как две другие получают все совпадения и позиции. Результаты для 1 миллиона итераций каждого из них были следующими: firstWordPosition() заняло 1,067 секунды, ответ slevy: 8,977 секунды, preg_match_all() 0,905 секунды.   -  person billynoah    schedule 19.06.2017
comment
Интересная статья об андроидах: androidauthority.com/beware-of-the- Benchmarks-604989 и может иметь некоторое применение за пределами этих устройств.   -  person slevy1    schedule 19.06.2017


Ответы (3)


Я думаю, вы могли бы, вероятно, просто удалить все пробельные символы, которые вам нужны (например, как насчет переносов?) и проверить " word ":

var_dump(firstWordPosition('Will company any do any job, (are there any)?', 'any'));
var_dump(firstWordPosition('Will *any* company do *any* job, (are there any)?', 'any'));

function firstWordPosition($str, $word) {
    // There are others, maybe also pass this in or array_merge() for more control.
    $nonchars = ["'",'"','.',',','!','?','(',')','^','$','#','\n','\r\n','\t',];
    // You could also do a strpos() with an if and another argument passed in.
    // Note that we're padding the $str to with spaces to match begin/end.
    $pos = stripos(str_replace($nonchars, ' ', " $str "), " $word ");

    // Have to account for the for-space on " $str ".
    return $pos ? $pos - 1: false;
}

Дает 12 (смещение от 0)

https://3v4l.org/qh9Rb

person Jared Farrish    schedule 13.06.2017
comment
Мне любопытно, почему у вас " $word " в stripos? Вы добавляете начальные и конечные пробелы? Это сбросит каждый индекс на единицу. - person fubar; 14.06.2017
comment
Вы видели редактирование? Замена не учитывает ни один из них, который будет пропущен. - person Jared Farrish; 14.06.2017
comment
Ах да, цель состоит в том, чтобы учесть граничное условие, например. слово . - person Jared Farrish; 14.06.2017
comment
Да, извините, только что понял. И вы добавляете пробелы в строку, потому что ищете слово с пробелами. Понятно. - person fubar; 14.06.2017

<?php
$subject = "any";
$b = " ";
$delimited = "$b$subject$b";

$replace = array("?","*","(",")",",",".");
$str = "Will *any* company do *any* job, (are there any)?";
echo "\nThe string: \"$str\"";

$temp = str_replace($replace,$b,$str);
while ( ($pos = strpos($temp,$delimited)) !== false )
{
    echo "\nThe subject \"$subject\" occurs at position ",($pos + 1);
    for ($i=0,$max=$pos + 1 + strlen($subject); $i <= $max; $i++) {
        $temp[$i] = $b;
    }
}

См. демонстрацию

Сценарий определяет границу слова как пробел. Если в строке есть неалфавитные символы, они заменяются пробелом, а результат сохраняется в $temp. По мере повторения цикла и обнаружения $subject каждый из его символов превращается в пробел, чтобы найти следующее появление субъекта. Учитывая объем проделанной работы, можно задаться вопросом, действительно ли такие усилия окупаются по сравнению с использованием регулярного выражения с функцией preg_. Это то, что каждый должен будет решить сам. Моя цель состояла в том, чтобы показать, как этого можно достичь с помощью strpos(), не прибегая к часто повторяемому общепринятому мнению SO, которое выступает за использование регулярных выражений.

Существует вариант, если вы ненавидите создавать замещающий массив неалфавитных символов следующим образом:

<?php

function getAllWholeWordPos($s,$word){
  $b = " ";
  $delimited = "$b$word$b";
  $retval = false;

  for ($i=0, $max = strlen( $s ); $i < $max; $i++) {
          if ( !ctype_alpha( $s[$i] ) ){
              $s[$i] = $b;
          }
  }

 while ( ( $pos = stripos( $s, $delimited) ) !== false ) {
    $retval[] = $pos + 1;
    for ( $i=0, $max = $pos + 1 + strlen( $word ); $i <= $max; $i++) {
        $s[$i] = $b;
    }
 }
 return $retval;
}

$whole_word = "any";    
$str = "Will *$whole_word* company do *$whole_word* job, (are there $whole_word)?";

echo "\nString: \"$str\""; 

$result = getAllWholeWordPos( $str, $whole_word );
$times = count( $result );
echo "\n\nThe word \"$whole_word\" occurs $times times:\n";
foreach ($result as $pos) { 
   echo "\nPosition: ",$pos;
}

См. демонстрацию

Обратите внимание, что этот пример с его обновлением улучшает код, предоставляя функцию, которая использует вариант strpos(), а именно stripos(), который имеет дополнительное преимущество, заключающееся в том, что он нечувствителен к регистру. Несмотря на более трудоемкое кодирование, производительность высокая; см. производительность.

person slevy1    schedule 13.06.2017
comment
зажигалка здесь полностью в глазах смотрящего. - person Jared Farrish; 14.06.2017
comment
Спасибо, но все, кто работает с PHP, знают, что функции preg_ тяжелее, чем использование strpos(). - person slevy1; 14.06.2017
comment
Да, и я уверен, что это отвлекает от всех остальных соображений. - person Jared Farrish; 14.06.2017
comment
Как насчет -, [, ], ', ", #, ~, |, _, @ и т. д. Вы собираетесь найти и заменить каждый небуквенно-цифровой символ? - person fubar; 14.06.2017
comment
@fubar - Спорно, видит ли кто-то некоторые из них как пробелы, поэтому иметь некоторый контроль может быть полезно. - person Jared Farrish; 14.06.2017
comment
Я понимаю вашу точку зрения, хотя в реальной жизни я видел очень большие массивы PHP, поэтому это возможно сделать. - person slevy1; 14.06.2017

Попробуйте следующий код

<!DOCTYPE html>
<html>
<body>

<?php
echo strpos("I love php, I love php     too!","php");
?> 

</body>
</html>


Output: 7
person Osama Khalid    schedule 13.06.2017