Почему не работает регулярка на php?

248
17 сентября 2017, 01:58

Регулярка на php должна находить слово "культурист" либо в начале строки, либо в конце либо, если оно в середине окружено круглыми скобками например: тест1 (культурист) тест2.

Подскажите, плиз, почему не работают обратные ссылки:

$str ="культурист тест1 (культурист) тест2";
$res = preg_match("/(культурист)$|^\\1|\(\\1\)/iu", $str, $match, 
PREG_OFFSET_CAPTURE);
print_r($match);

Не находит ничего (найдет только, если слово поставить в конце строки).

Answer 1
Небольшое упрощение

Для большей ясности сначала переведу Ваше регулярное выражение в одинарные кавычки:

'/(культурист)$|^\1|\(\1\)/iu'

Все дальнейшие регулярки тоже буду приводить в одинарных кавычках.

Устранение проблемы

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

'/(культурист)$|^(?1)|\((?1)\)/iu'

– то есть вместо обратных ссылок \1 следует использовать рекурсивные подмаски (?1). Разница в следующем:

  • обратные ссылки сопоставляются с конкретными текстовыми строками, которые были захвачены из текста подмаской
  • рекурсивные подмаски работают как "подпрограммы" – они подставляют указанную подмаску "как есть" на место вызова, независимо от того, какая строка была захвачена ей ранее (см.эквивалентную регулярку ниже)

Потестировать вживую предложенное решение можно здесь: https://regex101.com/r/3hOm5A/1 – заодно я добавил там тестовый пример со всеми кейсами.

Теперь главное: почему так происходит?

В своей регулярке Вы используете обратные ссылки совместно с альтернативным выбором. Как только первая альтернатива (культурист)$ отрабатывает, начинается вторая альтернатива ^\1. При переходе от альтернативы к альтернативе все обратные ссылки как бы "обнуляются". То есть считается, что в пределах данной (второй) альтернативы ещё никакой текст для первой подмаски не был найден. Подмаска есть, но обратная ссылка уже не хранит текст, который был ей сопоставлен в предыдущей альтернативе. То же самое происходит при переходе к третьей альтернативе. Именно поэтому две последних альтернативы у Вас не работают. Они просто не имеют доступа к ранее найденному тексту.

В свою очередь моё решение использует не обратные ссылки, а рекурсивные подмаски. В данном случае первая подмаска подставляется в альтернативные ветки как есть. В результате моя регулярка становится эквивалентна следующей:

'/(культурист)$|^культурист|\(культурист\)/iu'

– именно поэтому она отлично работает.

READ ALSO
yii2 передать параметры в get запрос activerecord

yii2 передать параметры в get запрос activerecord

Привет Нужно сделать выборку из БД по критериемт

280
Фильтрация yii2 выдает ошибку Call to a member function isAttributeRequired() on null

Фильтрация yii2 выдает ошибку Call to a member function isAttributeRequired() on null

Пытаюсь сделать фильтрацию на yii2Есть поле формы в ней 3 input(type="radio") , каждый инпут должен искать товары с ценой в данном диапазоне

547
Opencart модификация сайта

Opencart модификация сайта

Подскажите как в opencart сделать следующий функционал (можно готовые модули) сайт по продажи купонов:

180
RedBeanPHP ввод данных в кастомную таблицу

RedBeanPHP ввод данных в кастомную таблицу

Доброго времени сутокЧастенько делал регистрации на RedBeanPHP, и в данной ситуации тоже, но большое НО

232