Нужно сопоставить ВСЕ похожие слова/фразы, используя preg_match_all

Я пытаюсь создать шаблон, который соответствует всем похожим словам/фразам в строке.

Например, мне нужно сопоставить: «это», «это», «это то», «то», «это было», «это не было».

Он соответствует только первому вхождению "this", но должен соответствовать всем вхождениям.

Я даже пробовал якоря и границы слов, но ничего не работает.

Я пробовал (упрощенно):

$content = "this is it! that was not!";

preg_match_all('/(this|this is|this is it|that|that was|that was not)/i', $content, $results);

Что должно выводить:

  • это
  • это
  • это оно
  • тот
  • это было
  • это не было

person Tom    schedule 14.11.2014    source источник
comment
Используйте 1_   -  person hjpotter92    schedule 14.11.2014
comment
Вопрос: если вы собираетесь захватывать только те подстроки, которые ищете, почему бы не использовать цикл foreach и substr_count?   -  person Mr. Llama    schedule 14.11.2014
comment
@Мистер. Лама Не могли бы вы опубликовать пример?   -  person Tom    schedule 14.11.2014
comment
@Tom - Опубликовано как ответ: stackoverflow.com/a/26933945/477563   -  person Mr. Llama    schedule 14.11.2014


Ответы (4)


Учитывая, что вы фиксируете только те термины, которые ищете, может быть лучше просто использовать цикл foreach, а также substr_count, чтобы увидеть, сколько раз встречается каждая строка.

Например:

$haystack = "this is it! that was not! this is not a test!";
$needles = array(
    "this",
    "this is",
    "this is it",
    "that",
    "that was",
    "that was not");

foreach ($needles as $needle) {
    // substr_count is case sensitive, so make subject and search lowercase
    $hits = substr_count(strtolower($haystack), strtolower($needle));

    echo "Search '$needle' occurs $hits time(s)" . PHP_EOL;
}

Выше будет вывод:

Search 'this' occurs 2 time(s)
Search 'this is' occurs 2 time(s)
Search 'this is it' occurs 1 time(s)
Search 'that' occurs 1 time(s)
Search 'that was' occurs 1 time(s)
Search 'that was not' occurs 1 time(s)

Если substr_count не обеспечивает необходимой вам гибкости, вы всегда можете заменить его на preg_match_all и использовать свои индивидуальные значения $needle в качестве условия поиска.

person Mr. Llama    schedule 14.11.2014
comment
Спасибо за публикацию примера. Итак, если бы я хотел поместить все совпадения в массив, как бы я это сделал? Извините, я не лучший программист PHP. - person Tom; 14.11.2014
comment
Сами совпадения не будут иметь большого значения, потому что вы уже знаете, что они из себя представляют (потому что вы буквально только что искали их). Однако, если вы хотите сохранить их, вы можете сделать что-то вроде for ($i = 0; $i < $hits; $i++) { $matches[] = $needle; } сразу после оператора echo. - person Mr. Llama; 14.11.2014
comment
Спасибо, это работает. Как мне удалить повторяющиеся совпадения из массива? Итак, если это соответствует дважды, он сохраняет его только один раз в массиве? - person Tom; 14.11.2014
comment
Будет ли работать функция array_unique? Или есть что-то лучше? - person Tom; 14.11.2014
comment
@Tom - Просто удали цикл for и сделай его if ($hits > 0) { $matches[] = $needle; } - person Mr. Llama; 14.11.2014
comment
Когда совпадение не найдено в стоге сена, отображается сообщение об ошибке о том, что substr_count пуст. Есть ли способ исправить это? Должен ли оператор if иметь какой-либо оператор else? - person Tom; 15.11.2014
comment
Не обращайте внимания на мой последний вопрос. Спасибо большое за помощь! - person Tom; 18.11.2014

Проблема в том, что опция самой короткой строки появляется первой в вашей или группе:

/(this|this is|this is it)/i

PHP проверит, содержит ли тестовая строка элемент (this|this is|this is it) слева направо. Как только он найдет совпадение в тестовой строке, он покинет группу.

Это будет работать, потому что PHP сначала будет искать самую длинную строку:

/(this is it|this is|this)/i

Демо

введите здесь описание изображения

person idmean    schedule 14.11.2014
comment
Спасибо, это имеет смысл, но он все еще не показывает все возможные результаты в массиве. Любые идеи? - person Tom; 14.11.2014
comment
Ах, кажется, я понимаю, почему он показывает только это в массиве. Тестовая строка, которую я использую, содержит только это, тогда как ваша тестовая строка содержит каждое слово/фразу. Есть ли способ получить все совпадения только с этим? - person Tom; 14.11.2014

Как насчет:

$content = "this is it";
preg_match_all('/(?=(this))(?=(this is))(?=(this is it))/i', $content, $results);
print_r($results);

Редактировать в соответствии с комментариями:

$content = "this is it";
preg_match_all('/(?=(this))(?=(this is))(?=(this is it))|(?=(that))(?=(that was))(?=(that was not))/i', $content, $results);
print_r($results);

Вывод:

Array
(
    [0] => Array
        (
            [0] => 
            [1] => 
        )

    [1] => Array
        (
            [0] => this
            [1] => 
        )

    [2] => Array
        (
            [0] => this is
            [1] => 
        )

    [3] => Array
        (
            [0] => this is it
            [1] => 
        )

    [4] => Array
        (
            [0] => 
            [1] => that
        )

    [5] => Array
        (
            [0] => 
            [1] => that was
        )

    [6] => Array
        (
            [0] => 
            [1] => that was not
        )

)

Более универсальный:

$content = "this is it! that was not!";
preg_match_all('/\b(?=(\w+))(?=(\w+ \w+))(?=(\w+ \w+ \w+))\b/i', $content, $results);
print_r($results);

вывод:

Array
(
    [0] => Array
        (
            [0] => 
            [1] => 
        )

    [1] => Array
        (
            [0] => this
            [1] => that
        )

    [2] => Array
        (
            [0] => this is
            [1] => that was
        )

    [3] => Array
        (
            [0] => this is it
            [1] => that was not
        )

)
person Toto    schedule 14.11.2014
comment
Спасибо, это работает хорошо, но есть ли способ добавить в шаблон больше слов, которые не начинаются с этого? Например, ему также соответствует что-то другое (помимо вышеперечисленного): красное, красное яблоко, красная яблоня? - person Tom; 14.11.2014
comment
@Tom: Извини, я тебя не понимаю. Вы можете отредактировать свой вопрос и указать несколько примеров строк и ожидаемый результат. - person Toto; 14.11.2014
comment
Нет, он должен точно соответствовать этим словам/фразам. Ваш первый пример отлично работает, мне просто нужно добавить больше возможных совпадений с шаблоном. - person Tom; 14.11.2014
comment
Спасибо! Это прекрасно работает! Единственная проблема заключается в том, что если строка содержит только часть слова/фразы, она ей не соответствует. Например, если строка содержит только то, что было, она ничему не соответствует. Любые идеи? - person Tom; 14.11.2014

Вместо этого вы также можете использовать следующее регулярное выражение.

/(this(?:\sis(?:\sit)?)?)/i
person Nemesis    schedule 14.11.2014
comment
Но это вообще не отвечает на вопрос. - person idmean; 14.11.2014